<commit_before>extern crate time;
extern crate serde_json;

use std::io::prelude::*;

use std::time::{SystemTime, UNIX_EPOCH};

use chrono::{Local, TimeZone};

use std::fs;
use std::path::Path;
use std::error::Error;
use std::fs::OpenOptions;

use std::process::Command;

use std::fmt::Write as strwrite;

#[derive(Serialize, Deserialize, Debug)]
pub enum Event {
    Pause { time: u64, meta_info: Option<String> },
    Resume { time: u64 },
    Meta { time: u64, text: String },
    Commit { time: u64, hash: u64 },
    Branch { time: u64, name: String },
}

#[derive(Serialize, Deserialize, Debug)]
struct Session {
    start: u64,
    end: u64,
    running: bool,
    events: Vec<Event>,
}

impl Session {
    fn new() -> Session {
        let seconds = get_seconds();
        Session {
            start: seconds,
            end: seconds - 1,
            running: true,
            events: Vec::<Event>::new(),
        }
    }

    fn update_end(&mut self) {
        self.end = get_seconds();
    }

    pub fn is_running(&self) -> bool {
        self.running
    }

    fn finalize(&mut self) {
        if self.is_running() {
            if self.is_paused() {
                self.push_event(Event::Resume { time: get_seconds() });
            }

            self.running = false;
        }
    }

    fn status(&self) -> String {
        format!("{:?}", self)
    }

    fn is_paused(&self) -> bool {
        match self.events.len() {
            0 => false,
            n => {
                match self.events[n - 1] {
                    Event::Pause { .. } => true,
                    _ => false,
                }
            }
        }
    }

    fn push_event(&mut self, event: Event) -> bool {
        /* Cannot push if session is already finalized! */
        if !self.is_running() {
            println!("Already finalized, cannot push event");
            return false;
        }
        self.update_end();
        /* TODO: add logic */
        match event {
            Event::Pause { .. } => {
                if !self.is_paused() {
                    self.events.push(event);
                    true
                } else {
                    println!("Already paused!");
                    false
                }
            }
            Event::Resume { .. } => {
                if self.is_paused() {
                    self.events.push(event);
                    true
                } else {
                    println!("Currently not paused!");
                    false
                }
            }
            Event::Meta { time: ref metatime, text: ref meta_text /* TODO: rename */} => {
                if self.is_paused() {
                    /* Add metatext to pause (last elem of events vec) */
                    let len = self.events.len();
                    let pause = &mut self.events[len - 1];
                    match *pause {
                        Event::Pause { ref mut meta_info, .. } => {
                            match *meta_info {
                                Some(ref mut already) => {
                                    already.push_str("\n");
                                    already.push_str(meta_text);
                                }
                                None => *meta_info = Some(meta_text.clone()),
                            }
                        }
                        _ => unreachable!(),
                    };
                } else {
                    self.events.push(Event::Meta {
                                         time: *metatime,
                                         text: meta_text.clone(),
                                     })
                };
                true
            }
            Event::Commit { .. } |
            Event::Branch { .. } => {
                if self.is_paused() {
                    let now = get_seconds();
                    self.push_event(Event::Resume { time: now });
                }
                self.events.push(event);
                true
            }
        }
    }

    pub fn pause_time(&self) -> u64 {
        let mut pt = 0;
        let mut last_pause_ts = 0;
        for event in &self.events {
            match event {
                &Event::Pause { time, .. } => {
                    last_pause_ts = time;
                }
                &Event::Resume { time } => {
                    pt += time - last_pause_ts;
                }
                _ => {}
            }
        }
        pt
    }

    pub fn working_time(&self) -> u64 {
        let pt = self.pause_time();
        let wt = self.end - self.start;
        wt - pt
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Timesheet {
    start: u64,
    end: u64,
    user: String,
    sessions: Vec<Session>,
}

impl Timesheet {
    /** Initializes the .trk/timesheet.json file which holds
     * the serialized timesheet
     * Returns Some(newTimesheet) if operation succeeded */
    pub fn init(author_name: Option<&str>) -> Option<Timesheet> {
        // ts_to_date(get_seconds());
        /* Check if file already exists (no init permitted) */
        if Timesheet::is_init() {
            None
        } else {
            /* File does not exist, initialize */
            let git_author_name = &git_author().unwrap_or("".to_string());
            let author_name = match author_name {
                Some(name) => name,
                None => git_author_name,
            };
            let sheet = Timesheet {
                start: get_seconds(),
                end: get_seconds() - 1,
                user: author_name.to_string(),
                sessions: Vec::<Session>::new(),
            };
            if sheet.save_to_file() {
                Some(sheet)
            } else {
                None
            }
        }
    }

    fn is_init() -> bool {
        if Path::new("./.trk/timesheet.json").exists() {
            match Timesheet::load_from_file() {
                Some(..) => true,
                /* else, loading failed */
                None => false,
            }
        } else {
            /* File doesn't even exist */
            false
        }
    }

    // TODO: Check if this function could be used in more places
    fn get_last_session(&self) -> Option<&Session> {
        match self.sessions.len() {
            0 => None,
            n => Some(&self.sessions[n - 1]),
        }
    }

    fn get_last_session_mut(&mut self) -> Option<&mut Session> {
        match self.sessions.len() {
            0 => None,
            n => Some(&mut self.sessions[n - 1]),
        }
    }

    pub fn new_session(&mut self) -> bool {
        let n_sessions = self.sessions.len();
        let push = match n_sessions {
            0 => true,
            _ => {
                if self.sessions[n_sessions - 1].is_running() {
                    println!("Last session is still running!");
                    false
                } else {
                    true
                }
            }
        };
        if push {
            self.sessions.push(Session::new());
            self.save_to_file();
        }
        push
    }

    pub fn end_session(&mut self) {
        match self.get_last_session_mut() {
            Some(session) => {
                session.update_end();
                session.finalize();
            }
            None => println!("No session to finalize!"),
        }
        self.save_to_file();
    }

    pub fn pause(&mut self, metatext: Option<String>) {
        match self.get_last_session_mut() {
            Some(session) => {
                let now = get_seconds();
                session.push_event(Event::Pause { time: now, meta_info: metatext });
            }
            None => println!("No session to pause!"),
        }
        self.save_to_file();
    }

    pub fn resume(&mut self) {
        match self.get_last_session_mut() {
            Some(session) => {
                let now = get_seconds();
                session.push_event(Event::Resume { time: now });
            }
            None => println!("No session to pause!"),
        }
        self.save_to_file();
    }

    pub fn push_meta(&mut self, metatext: String) {
        match self.get_last_session_mut() {
            Some(session) => {
                let now = get_seconds();
                session.push_event(Event::Meta {
                                       time: now,
                                       text: metatext,
                                   });
            }
            None => println!("No session to add meta to!"),
        }
        self.save_to_file();
    }

    pub fn push_commit(&mut self, hash: u64) {
        match self.get_last_session_mut() {
            Some(session) => {
                let now = get_seconds();
                session.push_event(Event::Commit {
                                       time: now,
                                       hash: hash,
                                   });
            }
            None => println!("No session to add commit to!"),
        }
        self.save_to_file();
    }

    pub fn push_branch(&mut self, name: String) {
        match self.get_last_session_mut() {
            Some(session) => {
                let now = get_seconds();
                session.push_event(Event::Branch {
                                       time: now,
                                       name: name,
                                   });
            }
            None => println!("No session to change branch in!"),
        }
        self.save_to_file();
    }

    pub fn report(&self) -> bool {
        /* TODO: avoid time-of-check-to-time-of-use race risk */
        /* TODO: make all commands run regardless of where trk is executed
         * (and not just in root which is assumed here */

        let path = Path::new("./timesheet.html");
        let file = OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(&path);

        match file {
            Ok(mut file) => {
                file.write_all(self.to_html().as_bytes()).unwrap();
                /* save was successful */
                true
            }
            Err(why) => {
                println!("{}", why.description());
                false
            }
        }
    }

    pub fn last_session_report(&self) -> bool {
        let path = Path::new("./session.html");
        let file = OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(&path);

        match file {
            Ok(mut file) => {
                match self.get_last_session() {
                    Some(session) => {
                        file.write_all(session.to_html().as_bytes()).unwrap();
                        /* save was successful */
                        true
                    }
                    None => false,
                }
            }
            Err(why) => {
                println!("{}", why.description());
                false
            }
        }
    }

    fn save_to_file(&self) -> bool {
        /* TODO: avoid time-of-check-to-time-of-use race risk */
        /* TODO: make all commands run regardless of where trk is executed
         * (and not just in root which is assumed here */

        let path = Path::new("./.trk/timesheet.json");
        let file = OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(&path);

        match file {
            Ok(mut file) => {
                /* Convert the sheet to a JSON string. */
                let serialized =
                    serde_json::to_string(&self).expect("Could not write serialized time sheet!");
                file.write_all(serialized.as_bytes()).unwrap();
                /* save was successful */
                true
            }
            Err(why) => {
                println!("{}", why.description());
                false
            }
        }
    }

    /** Return a Some(Timesheet) struct if a timesheet.json file is present and valid
     * in the .trk directory, and None otherwise.
     * TODO: improve error handling
     * */
    pub fn load_from_file() -> Option<Timesheet> {
        let path = Path::new("./.trk/timesheet.json");
        let file = OpenOptions::new().read(true).open(&path);
        match file {
            Ok(mut file) => {
                let mut serialized = String::new();
                match file.read_to_string(&mut serialized) {
                    Ok(..) => serde_json::from_str(&serialized).unwrap_or(None),
                    Err(..) => {
                        println!("Reading the string failed!");
                        None
                    }
                }
            }
            Err(..) => {
                // println!("{}", why.description());
                None
            }
        }
    }

    pub fn clear() {
        /* Try to get name */
        let sheet = Timesheet::load_from_file();
        let name: Option<String> = sheet.map(|s| s.user.clone());

        let path = Path::new("./.trk/timesheet.json");
        if path.exists() {
            match fs::remove_file(&path) {
                Ok(..) => {}
                Err(why) => println!("Could not remove sessions file: {}", why.description()),
            }
        }
        match name {
            Some(name) => {
                /* Will overwrite file */
                Timesheet::init(Some(&name));
            }
            None => {
                Timesheet::init(None);
            }
        }
    }

    pub fn timesheet_status(&self) -> String {
        format!("{:?}", self)
    }

    pub fn last_session_status(&self) -> Option<String> {
        let n_sessions = self.sessions.len();
        match n_sessions {
            0 => None,
            n => {
                println!("{}", self.sessions[n - 1].working_time());
                Some(self.sessions[n - 1].status())
            }
        }
    }
}

trait HasTEX {
    fn to_tex(&self) -> String;
}

trait HasHTML {
    fn to_html(&self) -> String;
}

impl HasHTML for Event {
    fn to_html(&self) -> String {
        match self {
            &Event::Pause { time, ref meta_info } => {
                match meta_info {
                    &Some( ref info) => format!("
{}:\tStarted a pause
{}

",
                        ts_to_date(time), info.clone()),
                    &None => format!("
{}:\tStarted a pause
",
                        ts_to_date(time)),
                }
            }
            &Event::Resume { time } => {
                format!("
{}:\tResumed work
",
                        ts_to_date(time))
            }
            &Event::Meta { time, ref text } => {
                format!("
{}:\tNote: {}
",
                        ts_to_date(time),
                        text)
            }
            &Event::Commit { time, hash } => {
                format!("
{}:\tCommit id: {}
",
                        ts_to_date(time),
                        hash)
            }
            &Event::Branch { time, ref name } => {
                format!("
{}:\tBranch name: {}
",
                        ts_to_date(time),
                        name)
            }
        }
    }
}

impl HasHTML for Session {
    fn to_html(&self) -> String {
        let mut html = String::from(format!("
\n
Session on {}
\n",
                                            ts_to_date(self.start)));
        for event in &self.events {
            write!(&mut html, "{}\n", event.to_html()).unwrap();
        }
        write!(&mut html, "
").unwrap();
        html
    }
}

impl HasHTML for Timesheet {
    fn to_html(&self) -> String {
        let mut html = String::from("<!DOCTYPE html>\n");
        // TODO: non-hardcode that name!
        write!(&mut html, "\n\n<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">\n<title>Timesheet for Rafael Bachmann</title>").unwrap();
        write!(&mut html, "\n").unwrap();
        for session in &self.sessions {
            write!(&mut html, "{}\n", session.to_html()).unwrap();
        }
        write!(&mut html, "\n").unwrap();
        html
    }
}

fn get_seconds() -> u64 {
    SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs()
}

fn git_author() -> Option<String> {
    if let Ok(output) = Command::new("git").arg("config").arg("user.name").output() {
        if output.status.success() {
            let output = String::from_utf8_lossy(&output.stdout);
            /* remove trailing newline character */
            let mut output = output.to_string();
            output.pop().expect("Empty name in git config!?!");
            Some(output)
        } else {
            let output = String::from_utf8_lossy(&output.stderr);
            println!("git config user.name failed! {}", output);
            None
        }
    } else {
        None
    }
}

pub fn ts_to_date(timestamp: u64) -> String {
    Local.timestamp(timestamp as i64, 0).to_string()

}
<commit_msg>improve date formatting<commit_after>extern crate time;
extern crate serde_json;

use std::io::prelude::*;

use std::time::{SystemTime, UNIX_EPOCH};

use chrono::{Local, TimeZone};

use std::fs;
use std::path::Path;
use std::error::Error;
use std::fs::OpenOptions;

use std::process::Command;

use std::fmt::Write as strwrite;

#[derive(Serialize, Deserialize, Debug)]
pub enum Event {
    Pause { time: u64, meta_info: Option<String> },
    Resume { time: u64 },
    Meta { time: u64, text: String },
    Commit { time: u64, hash: u64 },
    Branch { time: u64, name: String },
}

#[derive(Serialize, Deserialize, Debug)]
struct Session {
    start: u64,
    end: u64,
    running: bool,
    events: Vec<Event>,
}

impl Session {
    fn new() -> Session {
        let seconds = get_seconds();
        Session {
            start: seconds,
            end: seconds - 1,
            running: true,
            events: Vec::<Event>::new(),
        }
    }

    fn update_end(&mut self) {
        self.end = get_seconds();
    }

    pub fn is_running(&self) -> bool {
        self.running
    }

    fn finalize(&mut self) {
        if self.is_running() {
            if self.is_paused() {
                self.push_event(Event::Resume { time: get_seconds() });
            }

            self.running = false;
        }
    }

    fn status(&self) -> String {
        format!("{:?}", self)
    }

    fn is_paused(&self) -> bool {
        match self.events.len() {
            0 => false,
            n => {
                match self.events[n - 1] {
                    Event::Pause { .. } => true,
                    _ => false,
                }
            }
        }
    }

    fn push_event(&mut self, event: Event) -> bool {
        /* Cannot push if session is already finalized! */
        if !self.is_running() {
            println!("Already finalized, cannot push event");
            return false;
        }
        self.update_end();
        /* TODO: add logic */
        match event {
            Event::Pause { .. } => {
                if !self.is_paused() {
                    self.events.push(event);
                    true
                } else {
                    println!("Already paused!");
                    false
                }
            }
            Event::Resume { .. } => {
                if self.is_paused() {
                    self.events.push(event);
                    true
                } else {
                    println!("Currently not paused!");
                    false
                }
            }
            Event::Meta { time: ref metatime, text: ref meta_text /* TODO: rename */} => {
                if self.is_paused() {
                    /* Add metatext to pause (last elem of events vec) */
                    let len = self.events.len();
                    let pause = &mut self.events[len - 1];
                    match *pause {
                        Event::Pause { ref mut meta_info, .. } => {
                            match *meta_info {
                                Some(ref mut already) => {
                                    already.push_str("\n");
                                    already.push_str(meta_text);
                                }
                                None => *meta_info = Some(meta_text.clone()),
                            }
                        }
                        _ => unreachable!(),
                    };
                } else {
                    self.events.push(Event::Meta {
                                         time: *metatime,
                                         text: meta_text.clone(),
                                     })
                };
                true
            }
            Event::Commit { .. } |
            Event::Branch { .. } => {
                if self.is_paused() {
                    let now = get_seconds();
                    self.push_event(Event::Resume { time: now });
                }
                self.events.push(event);
                true
            }
        }
    }

    pub fn pause_time(&self) -> u64 {
        let mut pt = 0;
        let mut last_pause_ts = 0;
        for event in &self.events {
            match event {
                &Event::Pause { time, .. } => {
                    last_pause_ts = time;
                }
                &Event::Resume { time } => {
                    pt += time - last_pause_ts;
                }
                _ => {}
            }
        }
        pt
    }

    pub fn working_time(&self) -> u64 {
        let pt = self.pause_time();
        let wt = self.end - self.start;
        wt - pt
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Timesheet {
    start: u64,
    end: u64,
    user: String,
    sessions: Vec<Session>,
}

impl Timesheet {
    /** Initializes the .trk/timesheet.json file which holds
     * the serialized timesheet
     * Returns Some(newTimesheet) if operation succeeded */
    pub fn init(author_name: Option<&str>) -> Option<Timesheet> {
        // ts_to_date(get_seconds());
        /* Check if file already exists (no init permitted) */
        if Timesheet::is_init() {
            None
        } else {
            /* File does not exist, initialize */
            let git_author_name = &git_author().unwrap_or("".to_string());
            let author_name = match author_name {
                Some(name) => name,
                None => git_author_name,
            };
            let sheet = Timesheet {
                start: get_seconds(),
                end: get_seconds() - 1,
                user: author_name.to_string(),
                sessions: Vec::<Session>::new(),
            };
            if sheet.save_to_file() {
                Some(sheet)
            } else {
                None
            }
        }
    }

    fn is_init() -> bool {
        if Path::new("./.trk/timesheet.json").exists() {
            match Timesheet::load_from_file() {
                Some(..) => true,
                /* else, loading failed */
                None => false,
            }
        } else {
            /* File doesn't even exist */
            false
        }
    }

    // TODO: Check if this function could be used in more places
    fn get_last_session(&self) -> Option<&Session> {
        match self.sessions.len() {
            0 => None,
            n => Some(&self.sessions[n - 1]),
        }
    }

    fn get_last_session_mut(&mut self) -> Option<&mut Session> {
        match self.sessions.len() {
            0 => None,
            n => Some(&mut self.sessions[n - 1]),
        }
    }

    pub fn new_session(&mut self) -> bool {
        let n_sessions = self.sessions.len();
        let push = match n_sessions {
            0 => true,
            _ => {
                if self.sessions[n_sessions - 1].is_running() {
                    println!("Last session is still running!");
                    false
                } else {
                    true
                }
            }
        };
        if push {
            self.sessions.push(Session::new());
            self.save_to_file();
        }
        push
    }

    pub fn end_session(&mut self) {
        match self.get_last_session_mut() {
            Some(session) => {
                session.update_end();
                session.finalize();
            }
            None => println!("No session to finalize!"),
        }
        self.save_to_file();
    }

    pub fn pause(&mut self, metatext: Option<String>) {
        match self.get_last_session_mut() {
            Some(session) => {
                let now = get_seconds();
                session.push_event(Event::Pause { time: now, meta_info: metatext });
            }
            None => println!("No session to pause!"),
        }
        self.save_to_file();
    }

    pub fn resume(&mut self) {
        match self.get_last_session_mut() {
            Some(session) => {
                let now = get_seconds();
                session.push_event(Event::Resume { time: now });
            }
            None => println!("No session to pause!"),
        }
        self.save_to_file();
    }

    pub fn push_meta(&mut self, metatext: String) {
        match self.get_last_session_mut() {
            Some(session) => {
                let now = get_seconds();
                session.push_event(Event::Meta {
                                       time: now,
                                       text: metatext,
                                   });
            }
            None => println!("No session to add meta to!"),
        }
        self.save_to_file();
    }

    pub fn push_commit(&mut self, hash: u64) {
        match self.get_last_session_mut() {
            Some(session) => {
                let now = get_seconds();
                session.push_event(Event::Commit {
                                       time: now,
                                       hash: hash,
                                   });
            }
            None => println!("No session to add commit to!"),
        }
        self.save_to_file();
    }

    pub fn push_branch(&mut self, name: String) {
        match self.get_last_session_mut() {
            Some(session) => {
                let now = get_seconds();
                session.push_event(Event::Branch {
                                       time: now,
                                       name: name,
                                   });
            }
            None => println!("No session to change branch in!"),
        }
        self.save_to_file();
    }

    pub fn report(&self) -> bool {
        /* TODO: avoid time-of-check-to-time-of-use race risk */
        /* TODO: make all commands run regardless of where trk is executed
         * (and not just in root which is assumed here */

        let path = Path::new("./timesheet.html");
        let file = OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(&path);

        match file {
            Ok(mut file) => {
                file.write_all(self.to_html().as_bytes()).unwrap();
                /* save was successful */
                true
            }
            Err(why) => {
                println!("{}", why.description());
                false
            }
        }
    }

    pub fn last_session_report(&self) -> bool {
        let path = Path::new("./session.html");
        let file = OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(&path);

        match file {
            Ok(mut file) => {
                match self.get_last_session() {
                    Some(session) => {
                        file.write_all(session.to_html().as_bytes()).unwrap();
                        /* save was successful */
                        true
                    }
                    None => false,
                }
            }
            Err(why) => {
                println!("{}", why.description());
                false
            }
        }
    }

    fn save_to_file(&self) -> bool {
        /* TODO: avoid time-of-check-to-time-of-use race risk */
        /* TODO: make all commands run regardless of where trk is executed
         * (and not just in root which is assumed here */

        let path = Path::new("./.trk/timesheet.json");
        let file = OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(&path);

        match file {
            Ok(mut file) => {
                /* Convert the sheet to a JSON string. */
                let serialized =
                    serde_json::to_string(&self).expect("Could not write serialized time sheet!");
                file.write_all(serialized.as_bytes()).unwrap();
                /* save was successful */
                true
            }
            Err(why) => {
                println!("{}", why.description());
                false
            }
        }
    }

    /** Return a Some(Timesheet) struct if a timesheet.json file is present and valid
     * in the .trk directory, and None otherwise.
     * TODO: improve error handling
     * */
    pub fn load_from_file() -> Option<Timesheet> {
        let path = Path::new("./.trk/timesheet.json");
        let file = OpenOptions::new().read(true).open(&path);
        match file {
            Ok(mut file) => {
                let mut serialized = String::new();
                match file.read_to_string(&mut serialized) {
                    Ok(..) => serde_json::from_str(&serialized).unwrap_or(None),
                    Err(..) => {
                        println!("Reading the string failed!");
                        None
                    }
                }
            }
            Err(..) => {
                // println!("{}", why.description());
                None
            }
        }
    }

    pub fn clear() {
        /* Try to get name */
        let sheet = Timesheet::load_from_file();
        let name: Option<String> = sheet.map(|s| s.user.clone());

        let path = Path::new("./.trk/timesheet.json");
        if path.exists() {
            match fs::remove_file(&path) {
                Ok(..) => {}
                Err(why) => println!("Could not remove sessions file: {}", why.description()),
            }
        }
        match name {
            Some(name) => {
                /* Will overwrite file */
                Timesheet::init(Some(&name));
            }
            None => {
                Timesheet::init(None);
            }
        }
    }

    pub fn timesheet_status(&self) -> String {
        format!("{:?}", self)
    }

    pub fn last_session_status(&self) -> Option<String> {
        let n_sessions = self.sessions.len();
        match n_sessions {
            0 => None,
            n => {
                println!("{}", self.sessions[n - 1].working_time());
                Some(self.sessions[n - 1].status())
            }
        }
    }
}

trait HasTEX {
    fn to_tex(&self) -> String;
}

trait HasHTML {
    fn to_html(&self) -> String;
}

impl HasHTML for Event {
    fn to_html(&self) -> String {
        match self {
            &Event::Pause { time, ref meta_info } => {
                match meta_info {
                    &Some( ref info) => format!("
{}:\tStarted a pause
{}

",
                        ts_to_date(time), info.clone()),
                    &None => format!("
{}:\tStarted a pause
",
                        ts_to_date(time)),
                }
            }
            &Event::Resume { time } => {
                format!("
{}:\tResumed work
",
                        ts_to_date(time))
            }
            &Event::Meta { time, ref text } => {
                format!("
{}:\tNote: {}
",
                        ts_to_date(time),
                        text)
            }
            &Event::Commit { time, hash } => {
                format!("
{}:\tCommit id: {}
",
                        ts_to_date(time),
                        hash)
            }
            &Event::Branch { time, ref name } => {
                format!("
{}:\tBranch name: {}
",
                        ts_to_date(time),
                        name)
            }
        }
    }
}

impl HasHTML for Session {
    fn to_html(&self) -> String {
        let mut html = String::from(format!("
\n
Session on {}
\n",
                                            ts_to_date(self.start)));
        for event in &self.events {
            write!(&mut html, "{}\n", event.to_html()).unwrap();
        }
        write!(&mut html, "
").unwrap();
        html
    }
}

impl HasHTML for Timesheet {
    fn to_html(&self) -> String {
        let mut html = String::from("<!DOCTYPE html>\n");
        // TODO: non-hardcode that name!
        write!(&mut html, "\n\n<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">\n<title>Timesheet for Rafael Bachmann</title>").unwrap();
        write!(&mut html, "\n").unwrap();
        for session in &self.sessions {
            write!(&mut html, "{}\n", session.to_html()).unwrap();
        }
        write!(&mut html, "\n").unwrap();
        html
    }
}

fn get_seconds() -> u64 {
    SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs()
}

fn git_author() -> Option<String> {
    if let Ok(output) = Command::new("git").arg("config").arg("user.name").output() {
        if output.status.success() {
            let output = String::from_utf8_lossy(&output.stdout);
            /* remove trailing newline character */
            let mut output = output.to_string();
            output.pop().expect("Empty name in git config!?!");
            Some(output)
        } else {
            let output = String::from_utf8_lossy(&output.stderr);
            println!("git config user.name failed! {}", output);
            None
        }
    } else {
        None
    }
}

pub fn ts_to_date(timestamp: u64) -> String {
    Local.timestamp(timestamp as i64, 0).format("%Y-%m-%d, %H:%M:%S%z").to_string()

}
<|endoftext|>


<commit_before>#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Timestamp(u64);

impl std::fmt::Display for Timestamp {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let u = self.0 / 1_000_000;
        let l = self.0 % 1_000_000;
        write!(f, "{}.{:06}", u, l)
    }
}

impl From<f64> for Timestamp {
    fn from(t: f64) -> Self {
        let micro_seconds = t * 1_000_000.0;
        Timestamp(micro_seconds as u64)
    }
}

impl From<u64> for Timestamp {
    fn from(t: u64) -> Self {
        let micro_seconds = t * 1_000_000;
        Timestamp(micro_seconds)
    }
}

impl From<(u64, f64)> for Timestamp {
    fn from(ts: (u64, f64)) -> Self {
        let (ti, td) = ts;
        let micro_seconds = ti * 1_000_000 + (td * 1_000_000.0) as u64;
        Timestamp(micro_seconds)
    }
}

impl<'de> ::serde::Deserialize<'de> for Timestamp {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: ::serde::Deserializer<'de>,
    {
        use serde::de::Error as SerdeError;

        // slack seems to use strings sometimes to
        // maintain precision greater than what f64 is happy with
        // so custom parser to get the f128 out of the string

        let value = ::serde_json::Value::deserialize(deserializer)?;

        if let Some(s) = value.as_str() {
            if let Some(dot_index) = s.find('.') {
                // must be f128 in string
                let i = s[..dot_index].parse::<u64>();
                let d = s[dot_index..].parse::<f64>();
                if let (Ok(i), Ok(d)) = (i, d) {
                    return Ok((i, d).into());
                }
            } else {
                // must be u64 in a string
                if let Ok(u) = s.parse::<u64>() {
                    return Ok(u.into());
                }
            }
        }

        if let Some(f) = value.as_f64() {
            Ok(f.into())
        } else if let Some(u) = value.as_u64() {
            Ok((u as f64).into())
        } else {
            Err(D::Error::custom(format!(
                "expected a timestamp but got: {}",
                value.to_string()
            )))
        }
    }
}

impl Timestamp {
    pub fn to_param_value(&self) -> String {
        format!("{}", self)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn preserve_precision_f64() {
        let ts_str = "1588859442.008705";
        let ts: Timestamp = serde_json::from_str(ts_str).unwrap();
        assert_eq!(ts, Timestamp(1588859442008705));
        assert_eq!(ts.to_param_value(), "1588859442.008705");
    }

    #[test]
    fn preserve_precision_str() {
        let ts_str = "\"1588859442.008705\"";
        let ts: Timestamp = serde_json::from_str(ts_str).unwrap();
        assert_eq!(ts, Timestamp(1588859442008705));
        assert_eq!(ts.to_param_value(), "1588859442.008705");
    }

    #[test]
    fn preserve_precision_str_0_dp() {
        let ts_str = "\"1588859442\"";
        let ts: Timestamp = serde_json::from_str(ts_str).unwrap();
        assert_eq!(ts, Timestamp(1588859442000000));
        assert_eq!(ts.to_param_value(), "1588859442.000000");
    }

    #[test]
    fn preserve_precision_str_1_dp() {
        let ts_str = "\"1588859442.1\"";
        let ts: Timestamp = serde_json::from_str(ts_str).unwrap();
        assert_eq!(ts, Timestamp(1588859442100000));
        assert_eq!(ts.to_param_value(), "1588859442.100000");
    }
}
<commit_msg>self not &self in Timestamp::to_param_value<commit_after>#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Timestamp(u64);

impl std::fmt::Display for Timestamp {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let u = self.0 / 1_000_000;
        let l = self.0 % 1_000_000;
        write!(f, "{}.{:06}", u, l)
    }
}

impl From<f64> for Timestamp {
    fn from(t: f64) -> Self {
        let micro_seconds = t * 1_000_000.0;
        Timestamp(micro_seconds as u64)
    }
}

impl From<u64> for Timestamp {
    fn from(t: u64) -> Self {
        let micro_seconds = t * 1_000_000;
        Timestamp(micro_seconds)
    }
}

impl From<(u64, f64)> for Timestamp {
    fn from(ts: (u64, f64)) -> Self {
        let (ti, td) = ts;
        let micro_seconds = ti * 1_000_000 + (td * 1_000_000.0) as u64;
        Timestamp(micro_seconds)
    }
}

impl<'de> ::serde::Deserialize<'de> for Timestamp {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: ::serde::Deserializer<'de>,
    {
        use serde::de::Error as SerdeError;

        // slack seems to use strings sometimes to
        // maintain precision greater than what f64 is happy with
        // so custom parser to get the f128 out of the string

        let value = ::serde_json::Value::deserialize(deserializer)?;

        if let Some(s) = value.as_str() {
            if let Some(dot_index) = s.find('.') {
                // must be f128 in string
                let i = s[..dot_index].parse::<u64>();
                let d = s[dot_index..].parse::<f64>();
                if let (Ok(i), Ok(d)) = (i, d) {
                    return Ok((i, d).into());
                }
            } else {
                // must be u64 in a string
                if let Ok(u) = s.parse::<u64>() {
                    return Ok(u.into());
                }
            }
        }

        if let Some(f) = value.as_f64() {
            Ok(f.into())
        } else if let Some(u) = value.as_u64() {
            Ok((u as f64).into())
        } else {
            Err(D::Error::custom(format!(
                "expected a timestamp but got: {}",
                value.to_string()
            )))
        }
    }
}

impl Timestamp {
    pub fn to_param_value(self) -> String {
        format!("{}", self)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn preserve_precision_f64() {
        let ts_str = "1588859442.008705";
        let ts: Timestamp = serde_json::from_str(ts_str).unwrap();
        assert_eq!(ts, Timestamp(1588859442008705));
        assert_eq!(ts.to_param_value(), "1588859442.008705");
    }

    #[test]
    fn preserve_precision_str() {
        let ts_str = "\"1588859442.008705\"";
        let ts: Timestamp = serde_json::from_str(ts_str).unwrap();
        assert_eq!(ts, Timestamp(1588859442008705));
        assert_eq!(ts.to_param_value(), "1588859442.008705");
    }

    #[test]
    fn preserve_precision_str_0_dp() {
        let ts_str = "\"1588859442\"";
        let ts: Timestamp = serde_json::from_str(ts_str).unwrap();
        assert_eq!(ts, Timestamp(1588859442000000));
        assert_eq!(ts.to_param_value(), "1588859442.000000");
    }

    #[test]
    fn preserve_precision_str_1_dp() {
        let ts_str = "\"1588859442.1\"";
        let ts: Timestamp = serde_json::from_str(ts_str).unwrap();
        assert_eq!(ts, Timestamp(1588859442100000));
        assert_eq!(ts.to_param_value(), "1588859442.100000");
    }
}
<|endoftext|>


<commit_before>use event::{Event, Rule, State};
use std::borrow::Cow::Borrowed;

fn is_ascii_whitespace(c: u8) -> bool {
    c == b'\n' || c == b'\r' || is_ascii_whitespace_no_nl(c)
}

fn is_ascii_whitespace_no_nl(c: u8) -> bool {
    c == b'\t' || c == 0x0b || c == 0x0c || c == b' '
}

// unusual among "scan" functions in that it scans from the _back_ of the string
// TODO: should also scan unicode whitespace?
fn scan_trailing_whitespace(data: &str) -> usize {
    match data.as_bytes().iter().rev().position(|&c| !is_ascii_whitespace_no_nl(c)) {
        Some(i) => i,
        None => data.len()
    }
}

#[derive(Debug)]
pub struct Tokenizer<'a> {
    sass: &'a str,
    bytes: &'a [u8],
    offset: usize,
    stack: Vec<Rule>,
    state: State,
}

impl<'a> Tokenizer<'a> {
    pub fn new(sass: &'a str) -> Tokenizer<'a> {
        Tokenizer {
            sass: &sass,
            bytes: &sass.as_bytes(),
            offset: 0,
            stack: Vec::new(),
            state: State::StartRule,
        }
    }

    fn start_rule(&mut self) -> Option<Event<'a>> {
        self.skip_leading_whitespace();

        if self.offset == self.sass.len() {
            return None
        }

        let c = self.bytes[self.offset];
        if c == b'}' {
            self.offset += 1;
            return Some(self.end())
        }
        if c == b'$' {
            return Some(self.next_variable())
        }

        let d = self.bytes[self.offset + 1];
        if c == b'/' && d == b'*' {
            return Some(self.next_comment())
        }

        self.state = State::InSelectors;
        self.stack.push(Rule::SassRule);

        Some(Event::Start(Rule::SassRule))
    }

    fn end(&mut self) -> Event<'a> {
        let rule = match self.stack.pop() {
            Some(r) => r,
            None => {
                println!("Unexpected empty stack!");
                return Event::End(Rule::SassRule)
            },
        };
        self.state = State::StartRule;
        Event::End(rule)
    }

    fn scan_while<F>(&mut self, data: &str, f: F) -> usize
            where F: Fn(u8) -> bool {
        match data.as_bytes().iter().position(|&c| !f(c)) {
            Some(i) => i,
            None => data.len()
        }
    }

    fn skip_leading_whitespace(&mut self) {
        let i = self.offset;
        self.offset += self.scan_while(&self.sass[i..self.sass.len()], is_ascii_whitespace);
    }

    fn next_comment(&mut self) -> Event<'a> {
        let comment_body_beginning = self.offset;
        let mut i = comment_body_beginning + 2;
        let limit = self.sass.len();

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b'*' ) {
                Some(pos) => { i += pos; },
                None => { break; },
            }

            if self.bytes[i+1] == b'/' {
                self.offset = i + 2;
                return Event::Comment(Borrowed(&self.sass[comment_body_beginning..i + 2]))
            } else {
                i += 1;
            }
        }
        Event::End(Rule::SassRule)
    }

    fn next_variable(&mut self) -> Event<'a> {
        // TODO: can parts of this be deduplicated with properties?
        let name_beginning = self.offset;
        let mut i = name_beginning;
        let limit = self.sass.len();

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b':' ) {
                Some(pos) => { i += pos; },
                None => { break; },
            }

            let name_end = i;

            i += 1;
            self.offset = i;
            self.skip_leading_whitespace();

            let value_beginning = self.offset;
            i = value_beginning;

            while i < limit {
                match self.bytes[i..limit].iter().position(|&c| c == b';') {
                    Some(pos) => { i += pos; },
                    None => { i = limit; break; },
                }

                let value_end = i;
                self.offset = i + 1;

                self.skip_leading_whitespace();

                return Event::Variable(
                    Borrowed(&self.sass[name_beginning..name_end]),
                    Borrowed(&self.sass[value_beginning..value_end])
                )
            }
        }
        self.offset = self.sass.len();
        Event::Property(Borrowed(""), Borrowed(""))
    }

    fn next_property(&mut self) -> Event<'a> {
        self.skip_leading_whitespace();

        let name_beginning = self.offset;
        let mut i = name_beginning;
        let limit = self.sass.len();

        let c = self.bytes[i];
        if c == b'}' {
            self.offset += 1;
            return self.end()
        }

        let d = self.bytes[i + 1];
        if c == b'/' && d == b'*' {
            return self.next_comment()
        }

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b':' || c == b'{') {
                Some(pos) => { i += pos; },
                None => { break; },
            }

            // Inefficient since we already skipped the whitespace and we'll have to
            // do it again but oh well
            let c = self.bytes[i];
            if c == b'{' {
                self.state = State::InSelectors;
                self.stack.push(Rule::SassRule);
                return Event::Start(Rule::SassRule)
            }

            let name_end = i;

            i += 1;
            self.offset = i;
            self.skip_leading_whitespace();

            let value_beginning = self.offset;
            i = value_beginning;

            while i < limit {
                match self.bytes[i..limit].iter().position(|&c| c == b';') {
                    Some(pos) => { i += pos; },
                    None => { i = limit; break; },
                }

                let value_end = i;
                self.offset = i + 1;

                self.skip_leading_whitespace();

                return Event::Property(
                    Borrowed(&self.sass[name_beginning..name_end]),
                    Borrowed(&self.sass[value_beginning..value_end])
                )

            }
        }
        self.offset = self.sass.len();
        Event::Property(Borrowed(""), Borrowed(""))
    }

    fn next_selector(&mut self) -> Event<'a> {
        let beginning = self.offset;
        let mut i = beginning;
        let limit = self.sass.len();

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b',' || c == b'{') {
                Some(pos) => { i += pos; },
                None => { i = limit; break; },
            }

            let c = self.bytes[i];

            if c == b',' || c == b'{' {
                let n = scan_trailing_whitespace(&self.sass[beginning..i]);
                let end = i - n;
                if end > beginning {
                    if c == b'{' {
                        self.state = State::InProperties;
                    }
                    self.offset = i + 1;
                    return Event::Selector(Borrowed(&self.sass[beginning..end]));
                }
            }

            self.offset = i;
            if i > beginning {
                return Event::Selector(Borrowed(&self.sass[beginning..i]))
            }
            i += 1;
        }

        if i > beginning {
            self.offset = i;
            Event::Selector(Borrowed(&self.sass[beginning..i]))
        } else {
            self.end()
        }
    }
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = Event<'a>;

    fn next(&mut self) -> Option<Event<'a>> {
        if self.offset < self.sass.len() {
            match self.state {
                State::StartRule => {
                    let ret = self.start_rule();
                    if ret.is_some() {
                        return ret
                    }
                },
                State::InSelectors => return Some(self.next_selector()),
                State::InProperties => return Some(self.next_property()),
            }
        }
        None
    }
}
<commit_msg>Extract a function to detect newlines<commit_after>use event::{Event, Rule, State};
use std::borrow::Cow::Borrowed;

fn is_ascii_whitespace(c: u8) -> bool {
   is_newline(c) || is_ascii_whitespace_no_nl(c)
}

fn is_ascii_whitespace_no_nl(c: u8) -> bool {
    c == b'\t' || c == 0x0b || c == 0x0c || c == b' '
}

fn is_newline(c: u8) -> bool {
    c == b'\n' || c == b'\r'
}

// unusual among "scan" functions in that it scans from the _back_ of the string
// TODO: should also scan unicode whitespace?
fn scan_trailing_whitespace(data: &str) -> usize {
    match data.as_bytes().iter().rev().position(|&c| !is_ascii_whitespace_no_nl(c)) {
        Some(i) => i,
        None => data.len()
    }
}

#[derive(Debug)]
pub struct Tokenizer<'a> {
    sass: &'a str,
    bytes: &'a [u8],
    offset: usize,
    stack: Vec<Rule>,
    state: State,
}

impl<'a> Tokenizer<'a> {
    pub fn new(sass: &'a str) -> Tokenizer<'a> {
        Tokenizer {
            sass: &sass,
            bytes: &sass.as_bytes(),
            offset: 0,
            stack: Vec::new(),
            state: State::StartRule,
        }
    }

    fn start_rule(&mut self) -> Option<Event<'a>> {
        self.skip_leading_whitespace();

        if self.offset == self.sass.len() {
            return None
        }

        let c = self.bytes[self.offset];
        if c == b'}' {
            self.offset += 1;
            return Some(self.end())
        }
        if c == b'$' {
            return Some(self.next_variable())
        }

        let d = self.bytes[self.offset + 1];
        if c == b'/' && d == b'*' {
            return Some(self.next_comment())
        }

        self.state = State::InSelectors;
        self.stack.push(Rule::SassRule);

        Some(Event::Start(Rule::SassRule))
    }

    fn end(&mut self) -> Event<'a> {
        let rule = match self.stack.pop() {
            Some(r) => r,
            None => {
                println!("Unexpected empty stack!");
                return Event::End(Rule::SassRule)
            },
        };
        self.state = State::StartRule;
        Event::End(rule)
    }

    fn scan_while<F>(&mut self, data: &str, f: F) -> usize
            where F: Fn(u8) -> bool {
        match data.as_bytes().iter().position(|&c| !f(c)) {
            Some(i) => i,
            None => data.len()
        }
    }

    fn skip_leading_whitespace(&mut self) {
        let i = self.offset;
        self.offset += self.scan_while(&self.sass[i..self.sass.len()], is_ascii_whitespace);
    }

    fn next_comment(&mut self) -> Event<'a> {
        let comment_body_beginning = self.offset;
        let mut i = comment_body_beginning + 2;
        let limit = self.sass.len();

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b'*' ) {
                Some(pos) => { i += pos; },
                None => { break; },
            }

            if self.bytes[i+1] == b'/' {
                self.offset = i + 2;
                return Event::Comment(Borrowed(&self.sass[comment_body_beginning..i + 2]))
            } else {
                i += 1;
            }
        }
        Event::End(Rule::SassRule)
    }

    fn next_variable(&mut self) -> Event<'a> {
        // TODO: can parts of this be deduplicated with properties?
        let name_beginning = self.offset;
        let mut i = name_beginning;
        let limit = self.sass.len();

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b':' ) {
                Some(pos) => { i += pos; },
                None => { break; },
            }

            let name_end = i;

            i += 1;
            self.offset = i;
            self.skip_leading_whitespace();

            let value_beginning = self.offset;
            i = value_beginning;

            while i < limit {
                match self.bytes[i..limit].iter().position(|&c| c == b';') {
                    Some(pos) => { i += pos; },
                    None => { i = limit; break; },
                }

                let value_end = i;
                self.offset = i + 1;

                self.skip_leading_whitespace();

                return Event::Variable(
                    Borrowed(&self.sass[name_beginning..name_end]),
                    Borrowed(&self.sass[value_beginning..value_end])
                )
            }
        }
        self.offset = self.sass.len();
        Event::Property(Borrowed(""), Borrowed(""))
    }

    fn next_property(&mut self) -> Event<'a> {
        self.skip_leading_whitespace();

        let name_beginning = self.offset;
        let mut i = name_beginning;
        let limit = self.sass.len();

        let c = self.bytes[i];
        if c == b'}' {
            self.offset += 1;
            return self.end()
        }

        let d = self.bytes[i + 1];
        if c == b'/' && d == b'*' {
            return self.next_comment()
        }

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b':' || c == b'{') {
                Some(pos) => { i += pos; },
                None => { break; },
            }

            // Inefficient since we already skipped the whitespace and we'll have to
            // do it again but oh well
            let c = self.bytes[i];
            if c == b'{' {
                self.state = State::InSelectors;
                self.stack.push(Rule::SassRule);
                return Event::Start(Rule::SassRule)
            }

            let name_end = i;

            i += 1;
            self.offset = i;
            self.skip_leading_whitespace();

            let value_beginning = self.offset;
            i = value_beginning;

            while i < limit {
                match self.bytes[i..limit].iter().position(|&c| c == b';') {
                    Some(pos) => { i += pos; },
                    None => { i = limit; break; },
                }

                let value_end = i;
                self.offset = i + 1;

                self.skip_leading_whitespace();

                return Event::Property(
                    Borrowed(&self.sass[name_beginning..name_end]),
                    Borrowed(&self.sass[value_beginning..value_end])
                )

            }
        }
        self.offset = self.sass.len();
        Event::Property(Borrowed(""), Borrowed(""))
    }

    fn next_selector(&mut self) -> Event<'a> {
        let beginning = self.offset;
        let mut i = beginning;
        let limit = self.sass.len();

        while i < limit {
            match self.bytes[i..limit].iter().position(|&c| c == b',' || c == b'{') {
                Some(pos) => { i += pos; },
                None => { i = limit; break; },
            }

            let c = self.bytes[i];

            if c == b',' || c == b'{' {
                let n = scan_trailing_whitespace(&self.sass[beginning..i]);
                let end = i - n;
                if end > beginning {
                    if c == b'{' {
                        self.state = State::InProperties;
                    }
                    self.offset = i + 1;
                    return Event::Selector(Borrowed(&self.sass[beginning..end]));
                }
            }

            self.offset = i;
            if i > beginning {
                return Event::Selector(Borrowed(&self.sass[beginning..i]))
            }
            i += 1;
        }

        if i > beginning {
            self.offset = i;
            Event::Selector(Borrowed(&self.sass[beginning..i]))
        } else {
            self.end()
        }
    }
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = Event<'a>;

    fn next(&mut self) -> Option<Event<'a>> {
        if self.offset < self.sass.len() {
            match self.state {
                State::StartRule => {
                    let ret = self.start_rule();
                    if ret.is_some() {
                        return ret
                    }
                },
                State::InSelectors => return Some(self.next_selector()),
                State::InProperties => return Some(self.next_property()),
            }
        }
        None
    }
}
<|endoftext|>


<commit_before>/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Michael Gehring <mg@ebfe.org>
 * (c) kwantam <kwantam@gmail.com>
 *     20150428 created `expand` module to eliminate most allocs during setup
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use std::char::from_u32;
use std::cmp::min;
use std::iter::Peekable;
use std::ops::Range;

#[inline]
fn unescape_char(c: char) -> char {
    match c {
        'a' => 0x07u8 as char,
        'b' => 0x08u8 as char,
        'f' => 0x0cu8 as char,
        'v' => 0x0bu8 as char,
        'n' => '\n',
        'r' => '\r',
        't' => '\t',
        _ => c,
    }
}

struct Unescape<'a> {
    string: &'a str,
}

impl<'a> Iterator for Unescape<'a> {
    type Item = char;

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        let slen = self.string.len();
        (min(slen, 1), None)
    }

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        if self.string.is_empty() {
            return None;
        }

        // is the next character an escape?
        let (ret, idx) = match self.string.chars().next().unwrap() {
            '\\' if self.string.len() > 1 => {
                // yes---it's \ and it's not the last char in a string
                // we know that \ is 1 byte long so we can index into the string safely
                let c = self.string[1..].chars().next().unwrap();
                (Some(unescape_char(c)), 1 + c.len_utf8())
            }
            c => (Some(c), c.len_utf8()), // not an escape char
        };

        self.string = &self.string[idx..]; // advance the pointer to the next char
        ret
    }
}

pub struct ExpandSet<'a> {
    range: Range<u32>,
    unesc: Peekable<Unescape<'a>>,
}

impl<'a> Iterator for ExpandSet<'a> {
    type Item = char;

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.unesc.size_hint()
    }

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        // while the Range has elements, try to return chars from it
        // but make sure that they actually turn out to be Chars!
        while let Some(n) = self.range.next() {
            if let Some(c) = from_u32(n) {
                return Some(c);
            }
        }

        if let Some(first) = self.unesc.next() {
            // peek ahead
            if self.unesc.peek() == Some(&'-')
                && match self.unesc.size_hint() {
                    (x, _) if x > 1 => true, // there's a range here; record it in our internal Range struct
                    _ => false,
                }
            {
                self.unesc.next(); // this is the '-'
                let last = self.unesc.next().unwrap(); // this is the end of the range

                #[allow(clippy::range_plus_one)]
                {
                    self.range = first as u32 + 1..last as u32 + 1;
                }
            }

            return Some(first); // in any case, return the next char
        }

        None
    }
}

impl<'a> ExpandSet<'a> {
    #[inline]
    pub fn new(s: &'a str) -> ExpandSet<'a> {
        ExpandSet {
            range: 0..0,
            unesc: Unescape { string: s }.peekable(),
        }
    }
}
<commit_msg>refactor/polish ~ fix `cargo clippy` complaints (fix/remove allow range_plus_one)<commit_after>/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Michael Gehring <mg@ebfe.org>
 * (c) kwantam <kwantam@gmail.com>
 *     20150428 created `expand` module to eliminate most allocs during setup
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use std::char::from_u32;
use std::cmp::min;
use std::iter::Peekable;
use std::ops::RangeInclusive;

#[inline]
fn unescape_char(c: char) -> char {
    match c {
        'a' => 0x07u8 as char,
        'b' => 0x08u8 as char,
        'f' => 0x0cu8 as char,
        'v' => 0x0bu8 as char,
        'n' => '\n',
        'r' => '\r',
        't' => '\t',
        _ => c,
    }
}

struct Unescape<'a> {
    string: &'a str,
}

impl<'a> Iterator for Unescape<'a> {
    type Item = char;

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        let slen = self.string.len();
        (min(slen, 1), None)
    }

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        if self.string.is_empty() {
            return None;
        }

        // is the next character an escape?
        let (ret, idx) = match self.string.chars().next().unwrap() {
            '\\' if self.string.len() > 1 => {
                // yes---it's \ and it's not the last char in a string
                // we know that \ is 1 byte long so we can index into the string safely
                let c = self.string[1..].chars().next().unwrap();
                (Some(unescape_char(c)), 1 + c.len_utf8())
            }
            c => (Some(c), c.len_utf8()), // not an escape char
        };

        self.string = &self.string[idx..]; // advance the pointer to the next char
        ret
    }
}

pub struct ExpandSet<'a> {
    range: RangeInclusive<u32>,
    unesc: Peekable<Unescape<'a>>,
}

impl<'a> Iterator for ExpandSet<'a> {
    type Item = char;

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.unesc.size_hint()
    }

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        // while the Range has elements, try to return chars from it
        // but make sure that they actually turn out to be Chars!
        while let Some(n) = self.range.next() {
            if let Some(c) = from_u32(n) {
                return Some(c);
            }
        }

        if let Some(first) = self.unesc.next() {
            // peek ahead
            if self.unesc.peek() == Some(&'-')
                && match self.unesc.size_hint() {
                    (x, _) if x > 1 => true, // there's a range here; record it in our internal Range struct
                    _ => false,
                }
            {
                self.unesc.next(); // this is the '-'
                let last = self.unesc.next().unwrap(); // this is the end of the range

                {
                    self.range = first as u32 + 1..=last as u32;
                }
            }

            return Some(first); // in any case, return the next char
        }

        None
    }
}

impl<'a> ExpandSet<'a> {
    #[inline]
    pub fn new(s: &'a str) -> ExpandSet<'a> {
        ExpandSet {
            range: 0..=0,
            unesc: Unescape { string: s }.peekable(),
        }
    }
}
<|endoftext|>


<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Joseph Crail <jbcrail@gmail.com>
// (c) Jian Zeng <anonymousknight96 AT gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//

#[cfg(unix)]
use super::libc;
use std::env;
use std::fs;
use std::io::{Error, ErrorKind};
use std::io::Result as IOResult;
use std::path::{Component, Path, PathBuf};
#[cfg(unix)]
use std::borrow::Cow;

pub fn resolve_relative_path<'a>(path: &'a Path) -> Cow<'a, Path> {
    if path.components().all(|e| e != Component::ParentDir) {
        return path.into();
    }
    let root = Component::RootDir.as_os_str();
    let mut result = env::current_dir().unwrap_or(PathBuf::from(root));
    for comp in path.components() {
        match comp {
            Component::ParentDir => {
                result.pop();
            }
            Component::CurDir => (),
            Component::RootDir |
            Component::Normal(_) |
            Component::Prefix(_) => result.push(comp.as_os_str()),
        }
    }
    result.into()
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CanonicalizeMode {
    None,
    Normal,
    Existing,
    Missing,
}

fn resolve<P: AsRef<Path>>(original: P) -> IOResult<PathBuf> {
    const MAX_LINKS_FOLLOWED: u32 = 255;
    let mut followed = 0;
    let mut result = original.as_ref().to_path_buf();
    loop {
        if followed == MAX_LINKS_FOLLOWED {
            return Err(Error::new(ErrorKind::InvalidInput, "maximum links followed"));
        }

        match fs::symlink_metadata(&result) {
            Err(e) => return Err(e),
            Ok(ref m) if !m.file_type().is_symlink() => break,
            Ok(..) => {
                followed += 1;
                match fs::read_link(&result) {
                    Ok(path) => {
                        result.pop();
                        result.push(path);
                    }
                    Err(e) => {
                        return Err(e);
                    }
                }
            }
        }
    }
    Ok(result)
}

pub fn canonicalize<P: AsRef<Path>>(original: P, can_mode: CanonicalizeMode) -> IOResult<PathBuf> {
    // Create an absolute path
    let original = original.as_ref();
    let original = if original.is_absolute() {
        original.to_path_buf()
    } else {
        env::current_dir().unwrap().join(original)
    };

    let mut result = PathBuf::new();
    let mut parts = vec![];

    // Split path by directory separator; add prefix (Windows-only) and root
    // directory to final path buffer; add remaining parts to temporary
    // vector for canonicalization.
    for part in original.components() {
        match part {
            Component::Prefix(_) |
            Component::RootDir => {
                result.push(part.as_os_str());
            }
            Component::CurDir => (),
            Component::ParentDir => {
                parts.pop();
            }
            Component::Normal(_) => {
                parts.push(part.as_os_str());
            }
        }
    }

    // Resolve the symlinks where possible
    if !parts.is_empty() {
        for part in parts[..parts.len() - 1].iter() {
            result.push(part);

            if can_mode == CanonicalizeMode::None {
                continue;
            }

            match resolve(&result) {
                Err(e) => {
                    match can_mode {
                        CanonicalizeMode::Missing => continue,
                        _ => return Err(e),
                    }
                }
                Ok(path) => {
                    result.pop();
                    result.push(path);
                }
            }
        }

        result.push(parts.last().unwrap());

        match resolve(&result) {
            Err(e) => {
                if can_mode == CanonicalizeMode::Existing {
                    return Err(e);
                }
            }
            Ok(path) => {
                result.pop();
                result.push(path);
            }
        }
    }
    Ok(result)
}

#[cfg(unix)]
pub fn is_stdin_interactive() -> bool {
    unsafe { libc::isatty(libc::STDIN_FILENO) == 1 }
}

#[cfg(windows)]
pub fn is_stdin_interactive() -> bool {
    false
}

#[cfg(unix)]
pub fn is_stdout_interactive() -> bool {
    unsafe { libc::isatty(libc::STDOUT_FILENO) == 1 }
}

#[cfg(windows)]
pub fn is_stdout_interactive() -> bool {
    false
}

#[cfg(unix)]
pub fn is_stderr_interactive() -> bool {
    unsafe { libc::isatty(libc::STDERR_FILENO) == 1 }
}

#[cfg(windows)]
pub fn is_stderr_interactive() -> bool {
    false
}
<commit_msg>uucore: read symlinked directories correctly in resolve_relative_path()<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Joseph Crail <jbcrail@gmail.com>
// (c) Jian Zeng <anonymousknight96 AT gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//

#[cfg(unix)]
use super::libc;
use std::env;
use std::fs;
use std::io::{Error, ErrorKind};
use std::io::Result as IOResult;
use std::path::{Component, Path, PathBuf};
#[cfg(unix)]
use std::borrow::Cow;

pub fn resolve_relative_path<'a>(path: &'a Path) -> Cow<'a, Path> {
    if path.components().all(|e| e != Component::ParentDir) {
        return path.into();
    }
    let root = Component::RootDir.as_os_str();
    let mut result = env::current_dir().unwrap_or(PathBuf::from(root));
    for comp in path.components() {
        match comp {
            Component::ParentDir => {
                if let Ok(p) = result.read_link() {
                    result = p;
                }
                result.pop();
            }
            Component::CurDir => (),
            Component::RootDir |
            Component::Normal(_) |
            Component::Prefix(_) => result.push(comp.as_os_str()),
        }
    }
    result.into()
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CanonicalizeMode {
    None,
    Normal,
    Existing,
    Missing,
}

fn resolve<P: AsRef<Path>>(original: P) -> IOResult<PathBuf> {
    const MAX_LINKS_FOLLOWED: u32 = 255;
    let mut followed = 0;
    let mut result = original.as_ref().to_path_buf();
    loop {
        if followed == MAX_LINKS_FOLLOWED {
            return Err(Error::new(ErrorKind::InvalidInput, "maximum links followed"));
        }

        match fs::symlink_metadata(&result) {
            Err(e) => return Err(e),
            Ok(ref m) if !m.file_type().is_symlink() => break,
            Ok(..) => {
                followed += 1;
                match fs::read_link(&result) {
                    Ok(path) => {
                        result.pop();
                        result.push(path);
                    }
                    Err(e) => {
                        return Err(e);
                    }
                }
            }
        }
    }
    Ok(result)
}

pub fn canonicalize<P: AsRef<Path>>(original: P, can_mode: CanonicalizeMode) -> IOResult<PathBuf> {
    // Create an absolute path
    let original = original.as_ref();
    let original = if original.is_absolute() {
        original.to_path_buf()
    } else {
        env::current_dir().unwrap().join(original)
    };

    let mut result = PathBuf::new();
    let mut parts = vec![];

    // Split path by directory separator; add prefix (Windows-only) and root
    // directory to final path buffer; add remaining parts to temporary
    // vector for canonicalization.
    for part in original.components() {
        match part {
            Component::Prefix(_) |
            Component::RootDir => {
                result.push(part.as_os_str());
            }
            Component::CurDir => (),
            Component::ParentDir => {
                parts.pop();
            }
            Component::Normal(_) => {
                parts.push(part.as_os_str());
            }
        }
    }

    // Resolve the symlinks where possible
    if !parts.is_empty() {
        for part in parts[..parts.len() - 1].iter() {
            result.push(part);

            if can_mode == CanonicalizeMode::None {
                continue;
            }

            match resolve(&result) {
                Err(e) => {
                    match can_mode {
                        CanonicalizeMode::Missing => continue,
                        _ => return Err(e),
                    }
                }
                Ok(path) => {
                    result.pop();
                    result.push(path);
                }
            }
        }

        result.push(parts.last().unwrap());

        match resolve(&result) {
            Err(e) => {
                if can_mode == CanonicalizeMode::Existing {
                    return Err(e);
                }
            }
            Ok(path) => {
                result.pop();
                result.push(path);
            }
        }
    }
    Ok(result)
}

#[cfg(unix)]
pub fn is_stdin_interactive() -> bool {
    unsafe { libc::isatty(libc::STDIN_FILENO) == 1 }
}

#[cfg(windows)]
pub fn is_stdin_interactive() -> bool {
    false
}

#[cfg(unix)]
pub fn is_stdout_interactive() -> bool {
    unsafe { libc::isatty(libc::STDOUT_FILENO) == 1 }
}

#[cfg(windows)]
pub fn is_stdout_interactive() -> bool {
    false
}

#[cfg(unix)]
pub fn is_stderr_interactive() -> bool {
    unsafe { libc::isatty(libc::STDERR_FILENO) == 1 }
}

#[cfg(windows)]
pub fn is_stderr_interactive() -> bool {
    false
}
<|endoftext|>


<commit_before>// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

enum_string! {
    CameraType {
        Orthographic = "orthographic",
        Perspective = "perspective",
    }
}

impl Default for CameraType {
    fn default() -> CameraType {
        CameraType::Perspective
    }
}

#[derive(Debug, Default, Deserialize, Serialize)]
pub struct Perspective {
    /// The floating-point aspect ratio of the field of view.
    ///
    /// When this is undefined, the aspect ratio of the canvas is used.
    #[serde(rename = "aspectRatio")]
    pub aspect_ratio: f32,

    /// The floating-point vertical field of view in radians.
    pub yfov: f32,

    /// The floating-point distance to the far clipping plane.
    ///
    /// `zfar` must be greater than `znear`.
    pub zfar: f32,
    /// The floating-point distance to the near clipping plane.
    ///
    /// `zfar` must be greater than `znear`.
    pub znear: f32,

    // TODO: extension
    // TODO: extras
}

#[derive(Debug, Default, Deserialize, Serialize)]
pub struct Orthographic {
    /// The floating-point horizontal magnification of the view.
    pub xmag: f32,

    /// The floating-point vertical magnification of the view.
    pub ymag: f32,

    /// The floating-point distance to the far clipping plane.
    pub zfar: f32,

    /// The floating-point distance to the near clipping plane.
    pub znear: f32,

    // TODO: extension
    // TODO: extras
}

#[derive(Debug, Default, Deserialize, Serialize)]
pub struct Camera {
    /// An orthographic camera containing properties to create an orthographic
    /// projection matrix.
    pub orthographic: Option<Orthographic>,

    /// A perspective camera containing properties to create a perspective
    /// projection matrix.
    pub perspective: Option<Perspective>,

    /// Specifies if the camera uses a perspective or orthographic projection.
    ///
    /// Based on this, either the camera's perspective or orthographic property
    /// will be defined.
    #[serde(rename = "type")]
    #[serde(default)]
    pub kind: CameraType,

    /// The user-defined name of this object.
    ///
    /// This is not necessarily unique, e.g., a camera and a buffer could have
    /// the same name, or two cameras could even have the same name.
    pub name: Option<String>,

    // TODO: extension
    // TODO: extras
}
<commit_msg>Rename xfov to x_fov<commit_after>// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

enum_string! {
    CameraType {
        Orthographic = "orthographic",
        Perspective = "perspective",
    }
}

impl Default for CameraType {
    fn default() -> CameraType {
        CameraType::Perspective
    }
}

#[derive(Debug, Default, Deserialize, Serialize)]
pub struct Perspective {
    /// The floating-point aspect ratio of the field of view.
    ///
    /// When this is undefined, the aspect ratio of the canvas is used.
    #[serde(rename = "aspectRatio")]
    pub aspect_ratio: f32,

    /// The floating-point vertical field of view in radians.
    #[serde(rename = "yfov")]
    pub y_fov: f32,

    /// The floating-point distance to the far clipping plane.
    ///
    /// `z_far` must be greater than `z_near`.
    #[serde(rename = "zfar")]
    pub z_far: f32,
    /// The floating-point distance to the near clipping plane.
    ///
    /// `z_far` must be greater than `z_near`.
    #[serde(rename = "znear")]
    pub z_near: f32,

    // TODO: extension
    // TODO: extras
}

#[derive(Debug, Default, Deserialize, Serialize)]
pub struct Orthographic {
    /// The floating-point horizontal magnification of the view.
    #[serde(rename = "xmag")]
    pub x_mag: f32,

    /// The floating-point vertical magnification of the view.
    #[serde(rename = "ymag")]
    pub y_mag: f32,

    /// The floating-point distance to the far clipping plane.
    #[serde(rename = "zfar")]
    pub z_far: f32,

    /// The floating-point distance to the near clipping plane.
    #[serde(rename = "znear")]
    pub z_near: f32,

    // TODO: extension
    // TODO: extras
}

#[derive(Debug, Default, Deserialize, Serialize)]
pub struct Camera {
    /// An orthographic camera containing properties to create an orthographic
    /// projection matrix.
    pub orthographic: Option<Orthographic>,

    /// A perspective camera containing properties to create a perspective
    /// projection matrix.
    pub perspective: Option<Perspective>,

    /// Specifies if the camera uses a perspective or orthographic projection.
    ///
    /// Based on this, either the camera's perspective or orthographic property
    /// will be defined.
    #[serde(rename = "type")]
    #[serde(default)]
    pub kind: CameraType,

    /// The user-defined name of this object.
    ///
    /// This is not necessarily unique, e.g., a camera and a buffer could have
    /// the same name, or two cameras could even have the same name.
    pub name: Option<String>,

    // TODO: extension
    // TODO: extras
}
<|endoftext|>


<commit_before>// Implements http://rosettacode.org/wiki/Hello_world/Web_server

use std::io::net::tcp::{TcpListener, TcpStream};
use std::io::net::ip::{Ipv4Addr, SocketAddr};
use std::io::{Acceptor, Listener};

fn handle_client(mut stream: TcpStream) {
    let response = bytes!("HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n<doctype !html><title>Bye-bye baby bye-bye</title>
Goodbye, world!
\r\n");
    match stream.write(response) {
        Ok(()) => println!("Response send!"),
        Err(e) => println!("Failed sending response: {}!", e),
    }
    drop(stream);
}

fn main() {
    let addr = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 8080 };
    let listener = TcpListener::bind(addr).unwrap();

    let mut acceptor = listener.listen();
    println!("Listening for connections on port {}", addr.port);

    for stream in acceptor.incoming() {
        spawn(proc() {
            handle_client(stream.unwrap());
        })
    }

    drop(acceptor);
}
<commit_msg>Improved webserver<commit_after>// Implements http://rosettacode.org/wiki/Hello_world/Web_server

use std::io::net::tcp::{TcpListener, TcpStream};
use std::io::net::ip::{Ipv4Addr, SocketAddr};
use std::io::{Acceptor, Listener};

fn handle_client(mut stream: TcpStream) {
    let response = bytes!(
"HTTP/1.1 200 OK
Content-Type: text/html;
charset=UTF-8

<doctype !html>

        <title>Bye-bye baby bye-bye</title>

Goodbye, world!

");
    match stream.write(response) {
        Ok(_) => println!("Response sent!"),
        Err(e) => println!("Failed sending response: {}!", e),
    }
}

fn main() {
    let addr = SocketAddr { ip: Ipv4Addr(127, 0, 0, 1), port: 80 };
    let listener = TcpListener::bind(addr).unwrap();

    let mut acceptor = listener.listen();
    println!("Listening for connections on port {}", addr.port);

    for stream in acceptor.incoming() {
        spawn(proc() {
            handle_client(stream.unwrap());
        });
    }
}
<|endoftext|>