<commit_before>use std::os;
use std::io::process::{Command,InheritFd,ExitStatus,ExitSignal};

use self::State::{Quoted, Escaped, Normal};

enum State<'a> {
    Quoted(&'a str),
    Escaped,
    Normal
}

fn trimmed(s: &str) -> String {
    from_str(s.trim()).unwrap()
}

fn extract_commands<'a>(input: &'a str) -> Vec<String> {
    let mut state: State = Normal;
    let mut commands: Vec<String> = vec![];
    let mut it = input.graphemes(true);
    let mut s = String::new();
    loop {
        let c = match it.next() {
            Some(ch) => ch.clone(),
            None => {
                commands.push(trimmed(s.as_slice()));
                return commands;
            }
        };
        match state {
            Normal => {
                match c {
                    "\\" => {
                        state = Escaped;
                    },
                    "'" | "\"" => {
                        state = Quoted(c);
                        s.push_str(c);
                    },
                    "," => {
                        commands.push(trimmed(s.as_slice()));
                        s.clear();
                    },
                    _ => {
                        s.push_str(c);
                    },
                }
            },
            Quoted(terminator) => {
                if c == terminator {
                    state = Normal;
                }
                s.push_str(c);
            },
            Escaped => {
                state = Normal;
                s.push_str(c);
            },
        }
    }
}

fn main() {
    // this gets invoked like this:
    //
    //   $ cargo-do cargo do [args]
    //
    // so we throw away the `cargo-do` and the `do`.
    // But, we keep the `cargo`, because we don't want
    // to just assume that `cargo` is the name of the cargo
    // binary, nor do we want to assume that it is on our path
    match os::args().as_slice() {
        [_, ref binname, _, rest..] => {
            let args = rest.connect(" ");
            for command in extract_commands(args.as_slice()).iter() {
                // need to improve this, it assumes cargo is in our path..
                let status = Command::new(binname)
                                .args(&[command])
                                .stdin(InheritFd(0))
                                .stdout(InheritFd(1))
                                .stderr(InheritFd(2))
                                .status();
                match status {
                    Ok(ExitStatus(0)) => (),
                    Ok(ExitStatus(i)) => {
                        os::set_exit_status(i)
                    },
                    Ok(ExitSignal(i)) => {
                        os::set_exit_status(i)
                    },
                    Err(_) => {
                        os::set_exit_status(127)
                    },
                }
            }
        },
        _ => {
            os::set_exit_status(127);
        }
    }
}

<commit_msg>remove outdated commit<commit_after>use std::os;
use std::io::process::{Command,InheritFd,ExitStatus,ExitSignal};

use self::State::{Quoted, Escaped, Normal};

enum State<'a> {
    Quoted(&'a str),
    Escaped,
    Normal
}

fn trimmed(s: &str) -> String {
    from_str(s.trim()).unwrap()
}

fn extract_commands<'a>(input: &'a str) -> Vec<String> {
    let mut state: State = Normal;
    let mut commands: Vec<String> = vec![];
    let mut it = input.graphemes(true);
    let mut s = String::new();
    loop {
        let c = match it.next() {
            Some(ch) => ch.clone(),
            None => {
                commands.push(trimmed(s.as_slice()));
                return commands;
            }
        };
        match state {
            Normal => {
                match c {
                    "\\" => {
                        state = Escaped;
                    },
                    "'" | "\"" => {
                        state = Quoted(c);
                        s.push_str(c);
                    },
                    "," => {
                        commands.push(trimmed(s.as_slice()));
                        s.clear();
                    },
                    _ => {
                        s.push_str(c);
                    },
                }
            },
            Quoted(terminator) => {
                if c == terminator {
                    state = Normal;
                }
                s.push_str(c);
            },
            Escaped => {
                state = Normal;
                s.push_str(c);
            },
        }
    }
}

fn main() {
    // this gets invoked like this:
    //
    //   $ cargo-do cargo do [args]
    //
    // so we throw away the `cargo-do` and the `do`.
    // But, we keep the `cargo`, because we don't want
    // to just assume that `cargo` is the name of the cargo
    // binary, nor do we want to assume that it is on our path
    match os::args().as_slice() {
        [_, ref binname, _, rest..] => {
            let args = rest.connect(" ");
            for command in extract_commands(args.as_slice()).iter() {
                let status = Command::new(binname)
                                .args(&[command])
                                .stdin(InheritFd(0))
                                .stdout(InheritFd(1))
                                .stderr(InheritFd(2))
                                .status();
                match status {
                    Ok(ExitStatus(0)) => (),
                    Ok(ExitStatus(i)) => {
                        os::set_exit_status(i)
                    },
                    Ok(ExitSignal(i)) => {
                        os::set_exit_status(i)
                    },
                    Err(_) => {
                        os::set_exit_status(127)
                    },
                }
            }
        },
        _ => {
            os::set_exit_status(127);
        }
    }
}

<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;

#[macro_use]
extern crate serde_derive;

extern crate serde_json;

mod timesheet;

fn main() {
    let mut session = timesheet::Session::new();

    /* Handle command line arguments with clap */
    let matches = clap_app!(trk =>
        (version: "0.1")
        (author: "Rafael B. <mediumendian@gmail.com>")
        (about: "Create timesheets from git history and meta info")
            (@arg CONFIG: -c --config +takes_value "[UNUSED] Sets a custom config file")
            (@arg debug: -d ... "[UNUSED] Sets the level of debugging information")

            (@subcommand beginperiod =>
                (about: "Begin period")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand beginsession =>
                (about: "Begin session")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand endperiod =>
                (about: "End period")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand endsession =>
                (about: "End session")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand pause =>
                (about: "Pause current session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
            (@subcommand proceed =>
                (about: "Proceed with currently paused session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
            (@subcommand meta =>
                (about: "Proceed with currently paused session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg text: +required "Meta information about work")
            )
            (@subcommand commit =>
                (about: "add a commit to the event list")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg hash: +required "Commit hash id")
            )
            (@subcommand branch =>
                (about: "add a topic to the event list")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg name: +required "New branch name")
            )
            (@subcommand status =>
                (about: "prints the current WIP")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
            (@subcommand clear =>
                (about: "temporary: clears the deserialized file")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
       )
            .get_matches();

    // Gets a value for config if supplied by user, or defaults to "default.conf"
    let config = matches.value_of("config").unwrap_or("default.conf");
    println!("[UNUSED] Value for config: {}", config);

    match matches.subcommand() {
        ("beginperiod", Some(..)) => session.push_event(timesheet::Event::BeginPeriod),
        ("endperiod", Some(..)) => session.push_event(timesheet::Event::EndPeriod),
        ("beginsession", Some(..)) => session.push_event(timesheet::Event::BeginSession),
        ("endsession", Some(..)) => session.push_event(timesheet::Event::EndSession),
        ("pause", Some(..)) => session.push_event(timesheet::Event::Pause),
        ("proceed", Some(..)) => session.push_event(timesheet::Event::Proceed),
        ("meta", Some(sub_input)) => {
            let metatext = sub_input.value_of("text").unwrap();
            session.push_event(timesheet::Event::Meta { text: metatext.to_string() });
        }
        ("commit", Some(sub_input)) => {
            let commit_hash = sub_input.value_of("hash").unwrap();
            let hash_parsed = u64::from_str_radix(commit_hash, 16).unwrap();
            session.push_event(timesheet::Event::Commit { hash: hash_parsed });
        }
        ("branch", Some(sub_input)) => {
            let branch_name = sub_input.value_of("name").unwrap();
            session.push_event(timesheet::Event::Branch { name: branch_name.to_string() });
        }
        ("status", Some(..)) => {
            session.status();
        }
        ("clear", Some(..)) => {
            // TODO: really do it
            println!("Clearing session and period!");
        }
        _ => {}
    }

    // Convert the session to a JSON string.
    let serialized = serde_json::to_string(&session).unwrap();

    // Prints serialized session
    println!("serialized = {}", serialized);

    // Convert the JSON string back to a Session.
    let deserialized: timesheet::Session = serde_json::from_str(&serialized).unwrap();

    // Prints deserialized Session
    println!("deserialized = {:?}", deserialized);
}
<commit_msg>add init command<commit_after>#[macro_use]
extern crate clap;

#[macro_use]
extern crate serde_derive;

extern crate serde_json;

mod timesheet;

fn main() {
    let mut session = timesheet::Session::new();

    /* Handle command line arguments with clap */
    let matches = clap_app!(trk =>
        (version: "0.1")
        (author: "Rafael B. <mediumendian@gmail.com>")
        (about: "Create timesheets from git history and meta info")
            (@arg CONFIG: -c --config +takes_value "[UNUSED] Sets a custom config file")
            (@arg debug: -d ... "[UNUSED] Sets the level of debugging information")

            (@subcommand init =>
                (about: "Initialise trk in this directory")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand beginperiod =>
                (about: "Begin period")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand beginsession =>
                (about: "Begin session")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand endperiod =>
                (about: "End period")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand endsession =>
                (about: "End session")
                (version: "0.1")
                (author:  "Rafael B. <mediumendian@gmail.com>")
            )
            (@subcommand pause =>
                (about: "Pause current session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
            (@subcommand proceed =>
                (about: "Proceed with currently paused session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
            (@subcommand meta =>
                (about: "Proceed with currently paused session")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg text: +required "Meta information about work")
            )
            (@subcommand commit =>
                (about: "add a commit to the event list")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg hash: +required "Commit hash id")
            )
            (@subcommand branch =>
                (about: "add a topic to the event list")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
                (@arg name: +required "New branch name")
            )
            (@subcommand status =>
                (about: "prints the current WIP")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
            (@subcommand clear =>
                (about: "temporary: clears the deserialized file")
                (version: "0.1")
                (author: "mediumendian@gmail.com")
            )
       )
            .get_matches();

    // Gets a value for config if supplied by user, or defaults to "default.conf"
    let config = matches.value_of("config").unwrap_or("default.conf");
    println!("[UNUSED] Value for config: {}", config);

    match matches.subcommand() {
        ("init", Some(..)) => timesheet::init(),
        ("beginperiod", Some(..)) => session.push_event(timesheet::Event::BeginPeriod),
        ("endperiod", Some(..)) => session.push_event(timesheet::Event::EndPeriod),
        ("beginsession", Some(..)) => session.push_event(timesheet::Event::BeginSession),
        ("endsession", Some(..)) => session.push_event(timesheet::Event::EndSession),
        ("pause", Some(..)) => session.push_event(timesheet::Event::Pause),
        ("proceed", Some(..)) => session.push_event(timesheet::Event::Proceed),
        ("meta", Some(sub_input)) => {
            let metatext = sub_input.value_of("text").unwrap();
            session.push_event(timesheet::Event::Meta { text: metatext.to_string() });
        }
        ("commit", Some(sub_input)) => {
            let commit_hash = sub_input.value_of("hash").unwrap();
            let hash_parsed = u64::from_str_radix(commit_hash, 16).unwrap();
            session.push_event(timesheet::Event::Commit { hash: hash_parsed });
        }
        ("branch", Some(sub_input)) => {
            let branch_name = sub_input.value_of("name").unwrap();
            session.push_event(timesheet::Event::Branch { name: branch_name.to_string() });
        }
        ("status", Some(..)) => {
            session.status();
        }
        ("clear", Some(..)) => {
            // TODO: really do it
            println!("Clearing session and period!");
        }
        _ => {}
    }

    // Convert the session to a JSON string.
    let serialized = serde_json::to_string(&session).unwrap();

    // Prints serialized session
    println!("serialized = {}", serialized);

    // Convert the JSON string back to a Session.
    let deserialized: timesheet::Session = serde_json::from_str(&serialized).unwrap();

    // Prints deserialized Session
    println!("deserialized = {:?}", deserialized);
}
<|endoftext|>


<commit_before>extern crate irc;

use std::io::{self, BufRead, BufReader};
use std::fs::File;
use std::thread;
use std::sync::mpsc::{channel, Sender};
use std::sync::Arc;
use std::collections::HashMap;
use irc::client::data::command::Command;
use irc::client::data::message::Message;
use irc::client::server::{IrcServer, NetIrcServer, Server};
use irc::client::server::utils::ServerExt;
use irc::client::data::kinds::{IrcRead, IrcWrite};

fn join_start_channels<T, U>(server: &IrcServer<T, U>) -> io::Result<()>
        where T: IrcRead, U: IrcWrite {
    let channels = BufReader::new(try!(File::open("channels.txt")));
    for line in channels.lines() {
        try!(server.send_join(&*try!(line)));
    }
    Ok(())
}

fn find_or_spawn<'a, S>(arc_irc_server: &Arc<NetIrcServer>,
                     user_senders: &'a mut HashMap<String, Sender<Message>>,
                     nick: S) -> &'a mut Sender<Message>
        where S: Into<String> {
    let nick = nick.into();
    user_senders.entry(nick).or_insert_with(|| {
        let (tx, rx) = channel();
        let irc_server = arc_irc_server.clone();
        thread::spawn(move || {
            for message in rx.iter() {
                if let Ok(command) = Command::from_message(&message) {
                    irc_server.send(command).unwrap();
                }
            }
        });
        tx
    })
}

fn main() {
    let arc_irc_server = Arc::new(IrcServer::new("config.json").unwrap());
    let server = arc_irc_server.clone();
    server.identify().unwrap();
    join_start_channels(&server).unwrap();
    let mut user_senders = HashMap::new();

    for message in server.iter() {
        let message = message.unwrap();
        if let Ok(command) = Command::from_message(&message) {
            match command {
                Command::PRIVMSG(_, msg) => {
                    if msg.starts_with("#") {
                        if let Some(chan) = message.get_source_nickname()
                                .map(|nick| find_or_spawn(&arc_irc_server,
                                                          &mut user_senders,
                                                          nick)) {
                            chan.send(message).unwrap();
                        }
                    }
                },
                _ => (),
            }
        }
    }
}
<commit_msg>Remove unecessary binding<commit_after>extern crate irc;

use std::io::{self, BufRead, BufReader};
use std::fs::File;
use std::thread;
use std::sync::mpsc::{channel, Sender};
use std::sync::Arc;
use std::collections::HashMap;
use irc::client::data::command::Command;
use irc::client::data::message::Message;
use irc::client::server::{IrcServer, NetIrcServer, Server};
use irc::client::server::utils::ServerExt;
use irc::client::data::kinds::{IrcRead, IrcWrite};

fn join_start_channels<T, U>(server: &IrcServer<T, U>) -> io::Result<()>
        where T: IrcRead, U: IrcWrite {
    let channels = BufReader::new(try!(File::open("channels.txt")));
    for line in channels.lines() {
        try!(server.send_join(&*try!(line)));
    }
    Ok(())
}

fn find_or_spawn<'a, S>(arc_irc_server: &Arc<NetIrcServer>,
                     user_senders: &'a mut HashMap<String, Sender<Message>>,
                     nick: S) -> &'a mut Sender<Message>
        where S: Into<String> {
    user_senders.entry(nick.into()).or_insert_with(|| {
        let (tx, rx) = channel();
        let irc_server = arc_irc_server.clone();
        thread::spawn(move || {
            for message in rx.iter() {
                if let Ok(command) = Command::from_message(&message) {
                    irc_server.send(command).unwrap();
                }
            }
        });
        tx
    })
}

fn main() {
    let arc_irc_server = Arc::new(IrcServer::new("config.json").unwrap());
    let server = arc_irc_server.clone();
    server.identify().unwrap();
    join_start_channels(&server).unwrap();
    let mut user_senders = HashMap::new();

    for message in server.iter() {
        let message = message.unwrap();
        if let Ok(command) = Command::from_message(&message) {
            match command {
                Command::PRIVMSG(_, msg) => {
                    if msg.starts_with("#") {
                        if let Some(chan) = message.get_source_nickname()
                                .map(|nick| find_or_spawn(&arc_irc_server,
                                                          &mut user_senders,
                                                          nick)) {
                            chan.send(message).unwrap();
                        }
                    }
                },
                _ => (),
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate clap;

use clap::{Arg, App};
use std::ffi::OsStr;
use std::fs::File;
use std::io;

fn main() {
    let app = App::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .author("Scott Olson <scott@solson.me>")
        .about(env!("CARGO_PKG_DESCRIPTION"))
        .arg(Arg::with_name("FILE")
             .multiple(true)
             .required(true)
             .help("The file(s) to display"));

    if std::env::args().len() == 1 {
        app.print_help().unwrap();
        println!("");
        return;
    }

    let matches = app.get_matches();
    let files: Vec<&OsStr> = matches.values_of_os("FILE").unwrap().collect();

    let mut first = true;
    for &filename in &files {
        if first { first = false; } else { println!(""); }

        if files.len() > 1 {
            println!("# {}", filename.to_string_lossy());
        }

        io::copy(&mut File::open(filename).unwrap(), &mut io::stdout()).unwrap();
    }
}
<commit_msg>Print help on no arguments in a better way.<commit_after>extern crate clap;

use clap::{Arg, App, AppSettings};
use std::ffi::OsStr;
use std::fs::File;
use std::io;

fn main() {
    let matches = App::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .author("Scott Olson <scott@solson.me>")
        .about(env!("CARGO_PKG_DESCRIPTION"))
        .setting(AppSettings::ArgRequiredElseHelp)
        .arg(Arg::with_name("FILE")
             .multiple(true)
             .required(true)
             .help("The file(s) to display"))
        .get_matches();

    let files: Vec<&OsStr> = matches.values_of_os("FILE").unwrap().collect();

    let mut first = true;
    for &filename in &files {
        if first { first = false; } else { println!(""); }

        if files.len() > 1 {
            println!("# {}", filename.to_string_lossy());
        }

        io::copy(&mut File::open(filename).unwrap(), &mut io::stdout()).unwrap();
    }
}
<|endoftext|>


<commit_before>#![feature(inclusive_range_syntax, iterator_step_by, range_contains)]
#![recursion_limit = "1024"]

extern crate alphred;
#[macro_use]
extern crate error_chain;
extern crate reqwest;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;

mod coordinate;
mod forecast;
mod errors;
mod sparkline;

use std::env;

use alphred::Item;

use coordinate::Coordinate;
use forecast::{Forecast, Icon};
use errors::*;

quick_main!(|| DarkSky::new()?.run());

struct DarkSky {
    dark_sky_api_key: String,
    location: Location,
}

impl DarkSky {
    fn new() -> Result<Self> {
        let dark_sky_api_key = env::var("DARK_SKY_API_KEY")?;
        let location = Self::location()?;

        Ok(Self {
            dark_sky_api_key,
            location,
        })
    }

    fn location() -> Result<Location> {
        let ip_info: IPInfo = reqwest::get("https://ipinfo.io/json")?.json()?;
        let description = format!("{}, {}", ip_info.city, ip_info.region);
        let coord = ip_info.coord;
        Ok(Location { description, coord })
    }

    fn run(&self) -> Result<()> {
        let forecast = self.forecast(self.location.coord.clone())?;

        let mut items = Vec::new();

        let item = Item::new(self.location.description.clone())
            .subtitle("• Powered by Dark Sky")
            .arg(&self.arg())
            .icon("icons/dark_sky.png");
        items.push(item);

        if let Some(item) = forecast.currently.and_then(|point| self.currently(&point)) {
            items.push(item);
        }

        if let Some(item) = forecast.minutely.and_then(|block| self.minutely(&block)) {
            items.push(item);
        }

        let json = json!({ "items": items });
        println!("{}", json);

        Ok(())
    }

    fn forecast(&self, coord: Coordinate) -> Result<Forecast> {
        let Coordinate(lat, long) = coord;
        let url = format!(
            "https://api.darksky.net/forecast/{}/{},{}",
            self.dark_sky_api_key,
            lat,
            long
        );
        Ok(reqwest::get(&url)?.json()?)
    }

    fn arg(&self) -> String {
        let Coordinate(lat, long) = self.location.coord;
        format!("{:.4},{:.4}", lat, long)
    }

    fn currently(&self, point: &forecast::Point) -> Option<Item> {
        if let (Some(title), Some(temp), Some(apparent_temp), Some(icon)) = (
            point.summary.clone(),
            point.temp,
            point.apparent_temp,
            point.icon.clone(),
        ) {
            let mut subtitle = vec![
                format!("{}°", temp.round()),
                format!("Feels like {}°", apparent_temp.round()),
            ];
            if let (Some(intensity), Some(probability)) = (
                point.precip_intensity.clone(),
                point.precip_probability.clone(),
            ) {
                if intensity.0 > 0. {
                    subtitle.push(format!(
                        "{} chance of {} rain.",
                        probability,
                        intensity.humanized()
                    ));
                }
            }
            let subtitle = subtitle.join(" · ");

            let mut item = Item::new(title);
            item = item.subtitle(&subtitle);
            item = item.arg(&self.arg());
            if let Some(path) = Self::translate_icon(&icon) {
                item = item.icon(format!("Dark-{}", path).as_str());
            }
            Some(item)
        } else {
            None
        }
    }

    fn minutely(&self, block: &forecast::Block) -> Option<Item> {
        if let (Some(title), Some(icon)) = (block.summary.clone(), block.icon.clone()) {
            let mut item = Item::new(title);

            let mut subtitle = Vec::new();
            let intensities = block.precip_intensities();
            if let (Some(min), Some(max)) = (intensities.iter().min(), intensities.iter().max()) {
                let sparkline = sparkline::Ascii::new(
                    min.0,
                    max.0,
                    intensities.clone().iter().map(|x| x.0).collect(),
                    4,
                );
                subtitle.push(format!("{} {} {}", min, sparkline, max));
            }
            let probabilities = block.precip_probabilities();
            if let (Some(min), Some(max)) = (probabilities.iter().min(), probabilities.iter().max())
            {
                let sparkline = sparkline::Ascii::new(
                    min.0,
                    1.,
                    probabilities.clone().iter().map(|x| x.0).collect(),
                    5,
                );
                subtitle.push(format!("{} {} {}", min, sparkline, max));
            }
            let subtitle = subtitle.join(" · ");

            item = item.subtitle(&subtitle);
            item = item.arg(&self.arg());
            if let Some(path) = Self::translate_icon(&icon) {
                item = item.icon(format!("Dark-{}", path).as_str());
            }
            Some(item)
        } else {
            None
        }
    }

    fn translate_icon(icon: &Icon) -> Option<String> {
        match *icon {
            Icon::ClearDay => Some("Sun"),
            Icon::ClearNight => Some("Moon"),
            Icon::Rain => Some("Cloud-Rain"),
            Icon::Snow => Some("Cloud-Snow"),
            Icon::Sleet => Some("Cloud-Snow-Alt"),
            Icon::Wind => Some("Wind"),
            Icon::Fog => Some("Cloud-Fog"),
            Icon::Cloudy => Some("Cloud"),
            Icon::PartlyCloudyDay => Some("Cloud-Sun"),
            Icon::PartlyCloudyNight => Some("Cloud-Moon"),
            Icon::Unknown(_) => None,
        }.map(String::from)
    }
}

#[derive(Debug)]
struct Location {
    description: String,
    coord: Coordinate,
}

#[derive(Debug, Deserialize)]
struct IPInfo {
    #[serde(rename = "loc")] coord: Coordinate,
    city: String,
    region: String,
}
<commit_msg>Handle theming in one place<commit_after>#![feature(inclusive_range_syntax, iterator_step_by, range_contains)]
#![recursion_limit = "1024"]

extern crate alphred;
#[macro_use]
extern crate error_chain;
extern crate reqwest;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;

mod coordinate;
mod forecast;
mod errors;
mod sparkline;

use std::env;

use alphred::Item;

use coordinate::Coordinate;
use forecast::{Forecast, Icon};
use errors::*;

quick_main!(|| DarkSky::new()?.run());

struct DarkSky {
    dark_sky_api_key: String,
    location: Location,
}

impl DarkSky {
    fn new() -> Result<Self> {
        let dark_sky_api_key = env::var("DARK_SKY_API_KEY")?;
        let location = Self::location()?;

        Ok(Self {
            dark_sky_api_key,
            location,
        })
    }

    fn location() -> Result<Location> {
        let ip_info: IPInfo = reqwest::get("https://ipinfo.io/json")?.json()?;
        let description = format!("{}, {}", ip_info.city, ip_info.region);
        let coord = ip_info.coord;
        Ok(Location { description, coord })
    }

    fn run(&self) -> Result<()> {
        let forecast = self.forecast(self.location.coord.clone())?;

        let mut items = Vec::new();

        let item = Item::new(self.location.description.clone())
            .subtitle("• Powered by Dark Sky")
            .arg(&self.arg())
            .icon("icons/dark_sky.png");
        items.push(item);

        if let Some(item) = forecast.currently.and_then(|point| self.currently(&point)) {
            items.push(item);
        }

        if let Some(item) = forecast.minutely.and_then(|block| self.minutely(&block)) {
            items.push(item);
        }

        let json = json!({ "items": items });
        println!("{}", json);

        Ok(())
    }

    fn forecast(&self, coord: Coordinate) -> Result<Forecast> {
        let Coordinate(lat, long) = coord;
        let url = format!(
            "https://api.darksky.net/forecast/{}/{},{}",
            self.dark_sky_api_key,
            lat,
            long
        );
        Ok(reqwest::get(&url)?.json()?)
    }

    fn arg(&self) -> String {
        let Coordinate(lat, long) = self.location.coord;
        format!("{:.4},{:.4}", lat, long)
    }

    fn currently(&self, point: &forecast::Point) -> Option<Item> {
        if let (Some(title), Some(temp), Some(apparent_temp), Some(icon)) = (
            point.summary.clone(),
            point.temp,
            point.apparent_temp,
            point.icon.clone(),
        ) {
            let mut subtitle = vec![
                format!("{}°", temp.round()),
                format!("Feels like {}°", apparent_temp.round()),
            ];
            if let (Some(intensity), Some(probability)) = (
                point.precip_intensity.clone(),
                point.precip_probability.clone(),
            ) {
                if intensity.0 > 0. {
                    subtitle.push(format!(
                        "{} chance of {} rain.",
                        probability,
                        intensity.humanized()
                    ));
                }
            }
            let subtitle = subtitle.join(" · ");

            let mut item = Item::new(title);
            item = item.subtitle(&subtitle);
            item = item.arg(&self.arg());
            if let Some(path) = Self::translate_icon(&icon) {
                item = item.icon(path.as_str());
            }
            Some(item)
        } else {
            None
        }
    }

    fn minutely(&self, block: &forecast::Block) -> Option<Item> {
        if let (Some(title), Some(icon)) = (block.summary.clone(), block.icon.clone()) {
            let mut item = Item::new(title);

            let mut subtitle = Vec::new();
            let intensities = block.precip_intensities();
            if let (Some(min), Some(max)) = (intensities.iter().min(), intensities.iter().max()) {
                let sparkline = sparkline::Ascii::new(
                    min.0,
                    max.0,
                    intensities.clone().iter().map(|x| x.0).collect(),
                    4,
                );
                subtitle.push(format!("{} {} {}", min, sparkline, max));
            }
            let probabilities = block.precip_probabilities();
            if let (Some(min), Some(max)) = (probabilities.iter().min(), probabilities.iter().max())
            {
                let sparkline = sparkline::Ascii::new(
                    min.0,
                    1.,
                    probabilities.clone().iter().map(|x| x.0).collect(),
                    5,
                );
                subtitle.push(format!("{} {} {}", min, sparkline, max));
            }
            let subtitle = subtitle.join(" · ");

            item = item.subtitle(&subtitle);
            item = item.arg(&self.arg());
            if let Some(path) = Self::translate_icon(&icon) {
                item = item.icon(path.as_str());
            }
            Some(item)
        } else {
            None
        }
    }

    fn translate_icon(icon: &Icon) -> Option<String> {
        match *icon {
            Icon::ClearDay => Some("Sun"),
            Icon::ClearNight => Some("Moon"),
            Icon::Rain => Some("Cloud-Rain"),
            Icon::Snow => Some("Cloud-Snow"),
            Icon::Sleet => Some("Cloud-Snow-Alt"),
            Icon::Wind => Some("Wind"),
            Icon::Fog => Some("Cloud-Fog"),
            Icon::Cloudy => Some("Cloud"),
            Icon::PartlyCloudyDay => Some("Cloud-Sun"),
            Icon::PartlyCloudyNight => Some("Cloud-Moon"),
            Icon::Unknown(_) => None,
        }.map(|x| format!("Dark-{}", x))
    }
}

#[derive(Debug)]
struct Location {
    description: String,
    coord: Coordinate,
}

#[derive(Debug, Deserialize)]
struct IPInfo {
    #[serde(rename = "loc")] coord: Coordinate,
    city: String,
    region: String,
}
<|endoftext|>


<commit_before>// `error_chain!` can recurse deeply
#![recursion_limit = "1024"]

extern crate argparse;
extern crate base64;
extern crate env_logger;
#[macro_use]
extern crate error_chain;
extern crate hyper;
#[macro_use]
extern crate log;
extern crate rand;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate serialport;
extern crate websocket;

mod serial_support;

use std::io::Write;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender};
use std::thread;
use std::time::Duration;

use argparse::{ArgumentParser, Store};
use websocket::client::Writer;
use websocket::result::WebSocketError;
use websocket::message::Type;
use websocket::{Server, Message};
use websocket::server::upgrade::WsUpgrade;
use hyper::Server as HttpServer;
use hyper::net::Fresh;
use hyper::server::request::Request;
use hyper::server::response::Response;
use rand::{thread_rng, Rng};

use serial_support::manage::Manager;
use serial_support::messages::*;
use serial_support::errors as e;

fn main() {

  // Init logger
  env_logger::init().unwrap();

  // let sr = SerialResponse::Response {
  //     data: "HEY YOU".to_string(),
  //     base64: None,
  // };
  // info!("serialized = {}", serde_json::to_string(&sr).unwrap());

  // Default port number
  let mut port = 8080;
  // Parse cmdline args
  {
    let mut ap = ArgumentParser::new();
    ap.set_description("Provide access to serial ports over JSON Websockets");
    ap.refer(&mut port)
      .add_option(&["-p", "--port"], Store, "Http Port");
    ap.parse_args_or_exit();
  }

  // websocket port
  let ws_port = port + 1;

  // html file for landing page
  let websocket_html = format!(include_str!("websockets.html"), ws_port = ws_port);

  // The HTTP server handler
  let http_handler = move |_: Request, response: Response<Fresh>| {
    let mut response = response.start().expect(&"Could not start response");
    // Send a client webpage
    response
      .write_all(websocket_html.as_bytes())
      .expect(&"Could not get template as bytes");
    response.end().expect(&"Send response failed");
  };

  info!("Using ports {} {}", port, ws_port);

  // Set up channels and Manager
  let (sub_tx, sub_rx) = channel::<SubscriptionRequest>();
  let (sreq_tx, sreq_rx) = channel::<SerialRequest>();
  Manager::spawn(sreq_rx, sub_rx);

  // Start listening for http connections
  thread::spawn(move || {
                  let http_server = HttpServer::http(format!("127.0.0.1:{}", port)).
                  expect(&format!("Failed to create http server on port {}",port));
                  http_server
                    .handle(http_handler)
                    .expect(&"Failed to listen");
                });

  // Start listening for WebSocket connections
  let ws_server = Server::bind(format!("127.0.0.1:{}", ws_port)).
  expect(&format!("Failed bind on websocket port {}",ws_port));

  for connection in ws_server.filter_map(Result::ok) {
    // Spawn a new thread for each connection.
    let sub_tx_clone = sub_tx.clone();
    let sreq_tx_clone = sreq_tx.clone();
    spawn_ws_handler(sub_tx_clone, sreq_tx_clone, connection);
  }
}

fn spawn_ws_handler(sub_tx_clone: Sender<SubscriptionRequest>,
                    sreq_tx_clone: Sender<SerialRequest>,
                    connection: WsUpgrade<TcpStream>) {
  thread::spawn(move || ws_handler(&sub_tx_clone, &sreq_tx_clone, connection));
}

fn ws_handler(sub_tx: &Sender<SubscriptionRequest>,
              sreq_tx: &Sender<SerialRequest>,
              connection: WsUpgrade<TcpStream>) {

  // Set up subscription id
  // let ts = SystemTime::now() - UNIX_EPOCH
  let prefix: String = thread_rng().gen_ascii_chars().take(8).collect();
  let sub_id = format!("thread-{}-{}", prefix, rand::random::<u16>());
  debug!("Spawned thread with subId '{}'", sub_id);

  if !connection
        .protocols()
        .contains(&"websocket-serial-json".to_string()) {
    connection
      .reject()
      .expect(&"Connection rejection failed.");
    return;
  }

  connection
    .tcp_stream()
    .set_nonblocking(true)
    .expect(&"Setting stream non-blocking failed.");

  // Create response channel
  let (sub_resp_tx, sub_resp_rx) = channel::<SerialResponse>();

  // Register sub_id with manager
  sub_tx
    .send(SubscriptionRequest {
            sub_id: sub_id.clone(),
            subscriber: sub_resp_tx,
          })
    .expect(&format!("{}: Registering with manager failed.", sub_id));

  let client = connection
    .use_protocol(format!("websocket-serial-json"))
    .accept()
    .expect(&format!("{}: Accept protocol failed.", sub_id));

  let ip = client
    .peer_addr()
    .expect(&format!("{}: Could not get peer address", sub_id));

  info!("{}: Connection from {}", sub_id, ip);

  //   let message = Message::text("Hello".to_string());
  //   client.send_message(&message).unwrap();

  let (mut receiver, mut sender) = client
    .split()
    .expect(&format!("{}: WS client error", sub_id));

  // TODO Check thread cleanup
  // The new WS Client thread msg loop
  let mut exit = false;

  let sleep_dur = Duration::from_millis(33);

  while !exit {
    // Try and read a WS message
    match receiver.recv_message::<Message, _, _>() {
      Ok(message) => {
        match message.opcode {

          Type::Close => {
            let message = Message::close();
            sender
              .send_message(&message)
              .unwrap_or(info!("{}: Client {} hung up!", sub_id, ip));
              // Send close request to cleanup resources
            sreq_tx.send(SerialRequest::Close{sub_id: sub_id.clone(), port:None})
            .unwrap_or_else(|e| warn!("Client exit cleanup failed for sub_id '{}', cause '{}'",sub_id,e));
            info!("{}: Client {} disconnected", sub_id, ip);
            exit = true;
          }

          Type::Ping => {
            let message = Message::pong(message.payload);
            sender
              .send_message(&message)
              .unwrap_or(info!("{}:  Could not ping client {}!", sub_id, ip));
          }

          _ => {

            // Get the payload, in a lossy manner
            let msg = String::from_utf8_lossy(&message.payload);

            // So we will get a result <SerialRequest::*,SerialResponse::Error> back
            match serde_json::from_str(&msg) {
              Ok(req) => {
                match sreq_tx.send(req) {
                  Err(err) => {
                    let error = e::ErrorKind::SendRequest(err).into();
                    send_serial_response_error(&sub_id, &mut sender, error);

                  }
                  _ => {}
                };
              }
              Err(err) => {
                let error = e::ErrorKind::Json(err).into();
                send_serial_response_error(&sub_id, &mut sender, error);
              }
            };
          }
        };
      }
      Err(e) => {
        match e {
          WebSocketError::NoDataAvailable => { /*Logging?*/ }
          _ => { /*Logging?*/ }
        };
      }
    }

    // Send on any serial responses
    match sub_resp_rx.try_recv() {
      Ok(resp) => {
        match serde_json::to_string(&resp) {
          Ok(json) => {
            let reply = Message::text(json.clone());
            // Weird, but message sending dose work
            // Do I misunderstand unwrap_or?
            sender
              .send_message(&reply)
              .unwrap_or_else(|e| {
                                info!("{}: Could not send message '{}' to client '{}', cause '{}'",
                                      sub_id,
                                      json,
                                      ip,
                                      e)
                              });
          }
          Err(_) => {}
        }
      }
      _ => { /*Logging*/ }
    };
    thread::sleep(sleep_dur);
  }

  info!("Thread {} exiting!", sub_id);

  fn send_serial_response_error(sub_id: &String, sender: &mut Writer<TcpStream>, error: e::Error) {
    let error = e::to_serial_response_error(error);
    serde_json::to_string(&error)
      .map_err(|err| e::ErrorKind::Json(err))
      .map(|json| Message::text(json))
      .map(|msg| {
             sender
               .send_message::<Message, _>(&msg)
               .map_err::<e::Error, _>(|err| e::ErrorKind::SendWsMessage(err).into())
           })
      .unwrap_or_else(|_| {
                        warn!("{}: Problem sending bad json error response", sub_id);
                        Ok(())
                      })
      .is_ok(); // This shouldn't be needed?

  }

}
<commit_msg>Format<commit_after>// `error_chain!` can recurse deeply
#![recursion_limit = "1024"]

extern crate argparse;
extern crate base64;
extern crate env_logger;
#[macro_use]
extern crate error_chain;
extern crate hyper;
#[macro_use]
extern crate log;
extern crate rand;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate serialport;
extern crate websocket;

mod serial_support;

use std::io::Write;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender};
use std::thread;
use std::time::Duration;

use argparse::{ArgumentParser, Store};
use websocket::client::Writer;
use websocket::result::WebSocketError;
use websocket::message::Type;
use websocket::{Server, Message};
use websocket::server::upgrade::WsUpgrade;
use hyper::Server as HttpServer;
use hyper::net::Fresh;
use hyper::server::request::Request;
use hyper::server::response::Response;
use rand::{thread_rng, Rng};

use serial_support::manage::Manager;
use serial_support::messages::*;
use serial_support::errors as e;

fn main() {

  // Init logger
  env_logger::init().unwrap();

  // let sr = SerialResponse::Response {
  //     data: "HEY YOU".to_string(),
  //     base64: None,
  // };
  // info!("serialized = {}", serde_json::to_string(&sr).unwrap());

  // Default port number
  let mut port = 8080;
  // Parse cmdline args
  {
    let mut ap = ArgumentParser::new();
    ap.set_description("Provide access to serial ports over JSON Websockets");
    ap.refer(&mut port)
      .add_option(&["-p", "--port"], Store, "Http Port");
    ap.parse_args_or_exit();
  }

  // websocket port
  let ws_port = port + 1;

  // html file for landing page
  let websocket_html = format!(include_str!("websockets.html"), ws_port = ws_port);

  // The HTTP server handler
  let http_handler = move |_: Request, response: Response<Fresh>| {
    let mut response = response.start().expect(&"Could not start response");
    // Send a client webpage
    response
      .write_all(websocket_html.as_bytes())
      .expect(&"Could not get template as bytes");
    response.end().expect(&"Send response failed");
  };

  info!("Using ports {} {}", port, ws_port);

  // Set up channels and Manager
  let (sub_tx, sub_rx) = channel::<SubscriptionRequest>();
  let (sreq_tx, sreq_rx) = channel::<SerialRequest>();
  Manager::spawn(sreq_rx, sub_rx);

  // Start listening for http connections
  thread::spawn(move || {
                  let http_server = HttpServer::http(format!("127.0.0.1:{}", port)).
                  expect(&format!("Failed to create http server on port {}",port));
                  http_server
                    .handle(http_handler)
                    .expect(&"Failed to listen");
                });

  // Start listening for WebSocket connections
  let ws_server = Server::bind(format!("127.0.0.1:{}", ws_port)).
  expect(&format!("Failed bind on websocket port {}",ws_port));

  for connection in ws_server.filter_map(Result::ok) {
    // Spawn a new thread for each connection.
    let sub_tx_clone = sub_tx.clone();
    let sreq_tx_clone = sreq_tx.clone();
    spawn_ws_handler(sub_tx_clone, sreq_tx_clone, connection);
  }
}

fn spawn_ws_handler(sub_tx_clone: Sender<SubscriptionRequest>,
                    sreq_tx_clone: Sender<SerialRequest>,
                    connection: WsUpgrade<TcpStream>) {
  thread::spawn(move || ws_handler(&sub_tx_clone, &sreq_tx_clone, connection));
}

fn ws_handler(sub_tx: &Sender<SubscriptionRequest>,
              sreq_tx: &Sender<SerialRequest>,
              connection: WsUpgrade<TcpStream>) {

  // Set up subscription id
  // let ts = SystemTime::now() - UNIX_EPOCH
  let prefix: String = thread_rng().gen_ascii_chars().take(8).collect();
  let sub_id = format!("thread-{}-{}", prefix, rand::random::<u16>());
  debug!("Spawned thread with subId '{}'", sub_id);

  if !connection
        .protocols()
        .contains(&"websocket-serial-json".to_string()) {
    connection
      .reject()
      .expect(&"Connection rejection failed.");
    return;
  }

  connection
    .tcp_stream()
    .set_nonblocking(true)
    .expect(&"Setting stream non-blocking failed.");

  // Create response channel
  let (sub_resp_tx, sub_resp_rx) = channel::<SerialResponse>();

  // Register sub_id with manager
  sub_tx
    .send(SubscriptionRequest {
            sub_id: sub_id.clone(),
            subscriber: sub_resp_tx,
          })
    .expect(&format!("{}: Registering with manager failed.", sub_id));

  let client = connection
    .use_protocol(format!("websocket-serial-json"))
    .accept()
    .expect(&format!("{}: Accept protocol failed.", sub_id));

  let ip = client
    .peer_addr()
    .expect(&format!("{}: Could not get peer address", sub_id));

  info!("{}: Connection from {}", sub_id, ip);

  //   let message = Message::text("Hello".to_string());
  //   client.send_message(&message).unwrap();

  let (mut receiver, mut sender) = client
    .split()
    .expect(&format!("{}: WS client error", sub_id));

  // TODO Check thread cleanup
  // The new WS Client thread msg loop
  let mut exit = false;

  let sleep_dur = Duration::from_millis(33);

  while !exit {
    // Try and read a WS message
    match receiver.recv_message::<Message, _, _>() {
      Ok(message) => {
        match message.opcode {

          Type::Close => {
            let message = Message::close();
            sender
              .send_message(&message)
              .unwrap_or(info!("{}: Client {} hung up!", sub_id, ip));
            // Send close request to cleanup resources
            sreq_tx
              .send(SerialRequest::Close {
                      sub_id: sub_id.clone(),
                      port: None,
                    })
              .unwrap_or_else(|e| {
                                warn!("Client exit cleanup failed for sub_id '{}', cause '{}'",
                                      sub_id,
                                      e)
                              });
            info!("{}: Client {} disconnected", sub_id, ip);
            exit = true;
          }

          Type::Ping => {
            let message = Message::pong(message.payload);
            sender
              .send_message(&message)
              .unwrap_or(info!("{}:  Could not ping client {}!", sub_id, ip));
          }

          _ => {

            // Get the payload, in a lossy manner
            let msg = String::from_utf8_lossy(&message.payload);

            // So we will get a result <SerialRequest::*,SerialResponse::Error> back
            match serde_json::from_str(&msg) {
              Ok(req) => {
                match sreq_tx.send(req) {
                  Err(err) => {
                    let error = e::ErrorKind::SendRequest(err).into();
                    send_serial_response_error(&sub_id, &mut sender, error);

                  }
                  _ => {}
                };
              }
              Err(err) => {
                let error = e::ErrorKind::Json(err).into();
                send_serial_response_error(&sub_id, &mut sender, error);
              }
            };
          }
        };
      }
      Err(e) => {
        match e {
          WebSocketError::NoDataAvailable => { /*Logging?*/ }
          _ => { /*Logging?*/ }
        };
      }
    }

    // Send on any serial responses
    match sub_resp_rx.try_recv() {
      Ok(resp) => {
        match serde_json::to_string(&resp) {
          Ok(json) => {
            let reply = Message::text(json.clone());
            // Weird, but message sending dose work
            // Do I misunderstand unwrap_or?
            sender
              .send_message(&reply)
              .unwrap_or_else(|e| {
                                info!("{}: Could not send message '{}' to client '{}', cause '{}'",
                                      sub_id,
                                      json,
                                      ip,
                                      e)
                              });
          }
          Err(_) => {}
        }
      }
      _ => { /*Logging*/ }
    };
    thread::sleep(sleep_dur);
  }

  info!("Thread {} exiting!", sub_id);

  fn send_serial_response_error(sub_id: &String, sender: &mut Writer<TcpStream>, error: e::Error) {
    let error = e::to_serial_response_error(error);
    serde_json::to_string(&error)
      .map_err(|err| e::ErrorKind::Json(err))
      .map(|json| Message::text(json))
      .map(|msg| {
             sender
               .send_message::<Message, _>(&msg)
               .map_err::<e::Error, _>(|err| e::ErrorKind::SendWsMessage(err).into())
           })
      .unwrap_or_else(|_| {
                        warn!("{}: Problem sending bad json error response", sub_id);
                        Ok(())
                      })
      .is_ok(); // This shouldn't be needed?

  }

}
<|endoftext|>


<commit_before>use std::ptr::null_mut;
use std::mem;
type Link<T> = Option<Box<Node<T>>>;

#[derive(Debug)]
struct Raw<T> {
    ptr: *const Node<T>,
}
impl<T> Raw<T> {
    fn none() -> Self {
        Raw { ptr: null_mut() }
    }
    fn some(ptr: &mut Node<T>) -> Self {
        Raw { ptr: ptr }
    }
    fn as_mut(&mut self) -> Option<&mut Node<T>> {
        unsafe {
            if self.ptr.is_null() {
                None
            } else {
                Some(&mut *(self.ptr as *mut Node<T>))
            }
        }

    }

    fn take(&mut self) -> Self {
        mem::replace(self, Raw::none())
    }
}

#[derive(Debug)]
struct Node<T> {
    elem: T,
    next: Link<T>,
    prev: Raw<T>,
}
impl<T> Node<T> {
    fn new(elem: T) -> Self {
        Node { elem: elem, next: None, prev: Raw::none() }
    }
    fn link(&mut self, mut next: Box<Self>) {
        next.prev = Raw::some(self);
        self.next = Some(next);
    }
}
#[derive(Debug)]
struct LinedList<T> {
    head: Link<T>,
    tail: Raw<T>,
    len: usize,
}

impl<T> LinedList<T> {
    fn new() -> Self {
        LinedList { head: None, tail: Raw::none(), len: 0 }
    }

    fn push_back(&mut self, elem: T) {
        self.len += 1;
        let mut node = Box::new(Node::new(elem));
        let mut old_tail = mem::replace(&mut self.tail, Raw::some(&mut *node));

        match old_tail.as_mut() {
            Some(tail) => tail.link(node),
            None => self.head = Some(node),
        }

    }

    fn pop_back(&mut self) -> Option<T> {
        self.tail.take().as_mut().and_then(|tail| {
            self.len -= 1;
            match tail.prev.as_mut() {
                Some(prev) => {
                    self.tail = Raw::some(prev);
                    prev.next.take().map(|node| node.elem)
                },
                None => self.head.take().map(|node| node.elem)
            }
        })
    }

    fn push_front(&mut self, elem: T) {
        self.len += 1;
        let mut new_head = Box::new(Node::new(elem));
        match self.head.take() {
            Some(head) => new_head.link(head),
            None => self.tail = Raw::some(&mut new_head)
        }
        self.head = Some(new_head);
    }

    fn pop_front(&mut self) -> Option<T> {
        self.head.take().and_then(|mut head| {
            // head is Box<Node<T>>
            head.next.take().map(|mut node| {
                node.prev = Raw::none();
                self.head = Some(node);
            });

            Some(head.elem)

        })
    }

}

/// 三种格式,
/// iter()   // over &T
/// iter_mut()   // over &mut T
/// into_iter()     // over T
///
/// 实现 Iterator
/// 1. 创建一个 struct 保存 iterator 状态
/// 2. 为这个 struct 实现 Iterator traid
fn main() {
    let mut l = LinedList::new();
    l.push_back(30);
    l.pop_front();
}

#[test]
fn push_pop_test() {
    let mut l = LinedList::new();
    l.push_back(30);
    l.push_back(20);
    assert_eq!(l.pop_back(), Some(20));
    assert_eq!(l.pop_back(), Some(30));
    l.push_back(22);
    assert_eq!(l.pop_back(), Some(22));
    assert_eq!(l.pop_back(), None);
    assert_eq!(l.pop_back(), None);
}

#[test]
fn push_front_test() {
    let mut list = LinedList::new();
    list.push_front(30);
    list.push_front(12);
    assert_eq!(list.pop_back(), Some(30));
    assert_eq!(list.pop_back(), Some(12));
}

#[test]
fn push_front_back_test() {
    let mut list = LinedList::new();
    list.push_front(30);
    list.push_front(12);
    list.push_back(22);
    assert_eq!(list.pop_back(), Some(22));
    assert_eq!(list.pop_back(), Some(30));
    assert_eq!(list.pop_back(), Some(12));
}

#[test]
fn pop_front_test() {
    let mut list = LinedList::new();
    list.push_front(30);
    list.push_front(12);
    list.push_back(22);
    assert_eq!(list.pop_front(), Some(12));
    assert_eq!(list.pop_front(), Some(30));
    assert_eq!(list.pop_front(), Some(22));
    assert_eq!(list.pop_front(), None);
}

<commit_msg>add pop front<commit_after>use std::ptr::null_mut;
use std::mem;
type Link<T> = Option<Box<Node<T>>>;

#[derive(Debug)]
struct Raw<T> {
    ptr: *const Node<T>,
}
impl<T> Raw<T> {
    fn none() -> Self {
        Raw { ptr: null_mut() }
    }
    fn some(ptr: &mut Node<T>) -> Self {
        Raw { ptr: ptr }
    }
    fn as_mut(&mut self) -> Option<&mut Node<T>> {
        unsafe {
            if self.ptr.is_null() {
                None
            } else {
                Some(&mut *(self.ptr as *mut Node<T>))
            }
        }

    }

    fn take(&mut self) -> Self {
        mem::replace(self, Raw::none())
    }
}

#[derive(Debug)]
struct Node<T> {
    elem: T,
    next: Link<T>,
    prev: Raw<T>,
}
impl<T> Node<T> {
    fn new(elem: T) -> Self {
        Node { elem: elem, next: None, prev: Raw::none() }
    }
    fn link(&mut self, mut next: Box<Self>) {
        next.prev = Raw::some(self);
        self.next = Some(next);
    }
    fn take_next(&mut self) -> Option<Box<Self>> {
        let mut next = self.next.take();
        next.as_mut().map(|node| {
            node.prev = Raw::none();
        });
        next
    }
}
#[derive(Debug)]
struct LinedList<T> {
    head: Link<T>,
    tail: Raw<T>,
    len: usize,
}

impl<T> LinedList<T> {
    fn new() -> Self {
        LinedList { head: None, tail: Raw::none(), len: 0 }
    }

    fn push_back(&mut self, elem: T) {
        self.len += 1;
        let mut node = Box::new(Node::new(elem));
        let mut old_tail = mem::replace(&mut self.tail, Raw::some(&mut *node));

        match old_tail.as_mut() {
            Some(tail) => tail.link(node),
            None => self.head = Some(node),
        }

    }

    fn pop_back(&mut self) -> Option<T> {
        self.tail.take().as_mut().and_then(|tail| {
            self.len -= 1;
            match tail.prev.as_mut() {
                Some(prev) => {
                    self.tail = Raw::some(prev);
                    prev.next.take().map(|node| node.elem)
                },
                None => self.head.take().map(|node| node.elem)
            }
        })
    }

    fn push_front(&mut self, elem: T) {
        self.len += 1;
        let mut new_head = Box::new(Node::new(elem));
        match self.head.take() {
            Some(head) => new_head.link(head),
            None => self.tail = Raw::some(&mut new_head)
        }
        self.head = Some(new_head);
    }

    fn pop_front(&mut self) -> Option<T> {
        self.head.take().and_then(|mut head| {
            // head is Box<Node<T>>
            self.len -= 1;
            head.take_next().map(|mut node| {
                node.prev = Raw::none();
                self.head = Some(node);
            });

            Some(head.elem)

        })
    }

}

/// 三种格式,
/// iter()   // over &T
/// iter_mut()   // over &mut T
/// into_iter()     // over T
///
/// 实现 Iterator
/// 1. 创建一个 struct 保存 iterator 状态
/// 2. 为这个 struct 实现 Iterator traid
fn main() {
    let mut l = LinedList::new();
    l.push_back(30);
    l.pop_front();
}

#[test]
fn push_pop_test() {
    let mut l = LinedList::new();
    l.push_back(30);
    assert_eq!(l.len, 1);
    l.push_back(20);
    assert_eq!(l.len, 2);
    assert_eq!(l.pop_back(), Some(20));
    assert_eq!(l.len, 1);
    assert_eq!(l.pop_back(), Some(30));
    assert_eq!(l.len, 0);
    l.push_back(22);
    assert_eq!(l.len, 1);
    assert_eq!(l.pop_back(), Some(22));
    assert_eq!(l.len, 0);
    assert_eq!(l.pop_back(), None);
    assert_eq!(l.len, 0);
    assert_eq!(l.pop_back(), None);
    assert_eq!(l.len, 0);
}

#[test]
fn push_front_test() {
    let mut list = LinedList::new();
    assert_eq!(list.len, 0);
    list.push_front(30);
    assert_eq!(list.len, 1);
    list.push_front(12);
    assert_eq!(list.len, 2);
    assert_eq!(list.pop_back(), Some(30));
    assert_eq!(list.len, 1);
    assert_eq!(list.pop_back(), Some(12));
    assert_eq!(list.len, 0);
}

#[test]
fn push_front_back_test() {
    let mut list = LinedList::new();
    assert_eq!(list.len, 0);
    list.push_front(30);
    assert_eq!(list.len, 1);
    list.push_front(12);
    assert_eq!(list.len, 2);
    list.push_back(22);
    assert_eq!(list.len, 3);
    assert_eq!(list.pop_back(), Some(22));
    assert_eq!(list.len, 2);
    assert_eq!(list.pop_back(), Some(30));
    assert_eq!(list.len, 1);
    assert_eq!(list.pop_back(), Some(12));
    assert_eq!(list.len, 0);
}

#[test]
fn pop_front_test() {
    let mut list = LinedList::new();
    list.push_front(30);
    list.push_front(12);
    list.push_back(22);
    assert_eq!(list.pop_front(), Some(12));
    assert_eq!(list.pop_front(), Some(30));
    assert_eq!(list.pop_front(), Some(22));
    assert_eq!(list.pop_front(), None);
}

<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
#[macro_use]
extern crate error_chain;
extern crate mhost;
extern crate resolv_conf;
extern crate tokio_core;
extern crate trust_dns;

use mhost::{multiple_lookup, DnsQuery};

use clap::{App, Arg, ArgMatches, Shell};
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::net::IpAddr;
use std::str::FromStr;
use std::time::Duration;
use tokio_core::reactor::Core;
use trust_dns::rr::{RData, RecordType};

// Arbitrary list of public DNS servers
static DEFAULT_DNS_SERVERS: &'static [&str] = &[
    // "Level3",
    "209.244.0.3",
    "209.244.0.4",
    // "Verisign",
    "64.6.64.6",
    "64.6.65.6",
    // "Google",
    "8.8.8.8",
    "8.8.4.4",
    // "DNS.WATCH",
    "84.200.69.80",
    "84.200.70.40",
    // "OpenDNS Home",
    "208.67.222.222",
    "208.67.220.220",
    // "SafeDNS",
    "195.46.39.39",
    "195.46.39.40",
    // "Dyn",
    "216.146.35.35",
    "216.146.36.36",
    // "FreeDNS",
    "37.235.1.174",
    "37.235.1.177",
    // "Alternate DNS",
    "198.101.242.72",
    "23.253.163.53",
];

static DEFAULT_RECORD_TYPES: &'static [&str] = &["a", "aaaa", "mx"];

fn run() -> Result<()> {
    let args = build_cli().get_matches();

    if args.is_present("completions") {
        return generate_completion(&args);
    }

    let dns_endpoints = get_dns_servers(&args).chain_err(|| ErrorKind::ServerIpAddrParsingError)?
        .into_iter()
        .map(|s| (s, 53))
        .collect();

    let timeout = value_t!(args, "timeout", u64).map(|t| Duration::from_secs(t)).unwrap();

    let mut query = match IpAddr::from_str(&args.value_of("domain name").unwrap()) {
        Ok(ip) => {
            DnsQuery::from(ip, vec![RecordType::PTR])
        }
        Err(_) => {
            let record_types = get_record_types(&args).chain_err(|| ErrorKind::ResoureRecordTypeParsingError)?;
            DnsQuery::new(&args.value_of("domain name").unwrap(), record_types)
        }
    };
    query = query.set_timeout(timeout);

    let mut io_loop = Core::new().unwrap();
    let lookup = multiple_lookup(&io_loop.handle(), query, dns_endpoints);
    let result = io_loop.run(lookup);

    match result {
        Ok(ref responses) => {
            for response in responses {
                match *response {
                    Ok(ref x) => println!("{}", DnsResponse(x)),
                    Err(ref e) => println!("Error: {}", e),
                }
            }
        }
        Err(_) => {
            println!("General Error");
        }
    }

    Ok(())
}

fn build_cli() -> App<'static, 'static> {
    let name = env!("CARGO_PKG_NAME");
    let version = env!("CARGO_PKG_VERSION");
    let about = env!("CARGO_PKG_DESCRIPTION");

    App::new(name)
        .version(version)
        .about(about)
        .arg(Arg::with_name("domain name")
            .index(1)
            .required(true)
            .conflicts_with("completions")
            .help("domain name or IP address to look up"))
        .arg(Arg::with_name("timeout")
            .long("timeout")
            .default_value("5")
            .help("timeout for server responses in sec")
            .takes_value(true))
        .arg(Arg::with_name("record types")
            .long("type")
            .short("t")
            .takes_value(true)
            .multiple(true)
            .number_of_values(1)
            .possible_values(&["a", "aaaa", "any", "cname", "dnskey", "mx", "ns", "opt", "ptr", "soa", "srv", "txt"])
            .help("Select resource record type [default: a, aaaa, mx]"))
        .arg(Arg::with_name("dont use local dns servers")
            .short("L")
            .help("Do not use local (/etc/resolv.conf) DNS servers"))
        .arg(Arg::with_name("predefined server")
            .short("S")
            .help("use predefined DNS server set"))
        .arg(Arg::with_name("DNS servers")
            .long("server")
            .short("s")
            .takes_value(true)
            .multiple(true)
            .number_of_values(1)
            .help("DNS servers to use; if empty use predefined, public DNS server"))
        .arg(Arg::with_name("completions")
            .long("completions")
            .takes_value(true)
            .hidden(true)
            .possible_values(&["bash", "fish", "zsh"])
            .help("The shell to generate the script for"))
}

fn generate_completion(args: &ArgMatches) -> Result<()> {
    let bin_name = env!("CARGO_PKG_NAME");
    let shell = args.value_of("completions").ok_or(
        ErrorKind::CliArgsParsingError,
    )?;
    build_cli().gen_completions_to(
        bin_name,
        shell.parse::<Shell>().unwrap(),
        &mut std::io::stdout(),
    );
    Ok(())
}

fn get_local_dns_servers() -> Result<Vec<IpAddr>> {
    let mut buf = Vec::with_capacity(4096);
    let mut f = File::open("/etc/resolv.conf").chain_err(|| ErrorKind::ResolvConfError)?;
    f.read_to_end(&mut buf).unwrap();
    let cfg = resolv_conf::Config::parse(&buf[..]).chain_err(|| ErrorKind::ResolvConfError)?;
    Ok(cfg.nameservers)
}

fn get_dns_servers(args: &ArgMatches) -> Result<Vec<IpAddr>> {
    let mut dns_servers: Vec<IpAddr> = Vec::new();
    if let Some(servers) = args.values_of_lossy("DNS servers") {
        dns_servers.extend(servers
            .into_iter()
            .map(|server|
                IpAddr::from_str(&server).unwrap()
            )
        );
    }
    if args.is_present("predefined server") {
        dns_servers.extend(DEFAULT_DNS_SERVERS
            .iter()
            .map(|server| IpAddr::from_str(server).unwrap())
        );
    }
    if !args.is_present("dont use local dns servers") {
        dns_servers.extend(get_local_dns_servers()?);
    }

    Ok(dns_servers)
}

fn get_record_types(args: &ArgMatches) -> Result<Vec<RecordType>> {
    let record_types = if let Some(record_types) = args.values_of_lossy("record types") {
        record_types
            .iter()
            .map(|rt| RecordType::from_str(&rt.to_uppercase()).unwrap())
            .collect()
    } else {
        DEFAULT_RECORD_TYPES
            .iter()
            .map(|rt| RecordType::from_str(&rt.to_uppercase()).unwrap())
            .collect()
    };

    Ok(record_types)
}

// Newtype pattern for Display implementation
struct DnsResponse<'a>(pub &'a mhost::DnsResponse);

// Display impl for plain, basic output
impl<'a> fmt::Display for DnsResponse<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let DnsResponse(dns_response) = *self;
        if dns_response.answers.is_empty() {
            return write!(f, "DNS server {} has not records.", dns_response.server);
        }
        let _ = write!(f, "DNS server {} responded with\n", dns_response.server);
        let mut answers: Vec<String> = dns_response
            .answers
            .iter()
            .map(|answer| match *answer.rdata() {
                RData::A(ip) => format!(" * IPv4: {}", ip),
                RData::AAAA(ip) => format!(" * IPv6: {}", ip),
                RData::CNAME(ref name) => format!(" * CNAME: {}", name),
                RData::MX(ref mx) => {
                    format!(
                        " * MX: {} with preference {}",
                        mx.exchange(),
                        mx.preference()
                    )
                }
                RData::NS(ref name) => format!(" * NS: {}", name),
                RData::SOA(ref soa) => {
                    format!(
                        " * SOA: {} {} {} {} {} {} {}",
                        soa.mname(),
                        soa.rname(),
                        soa.serial(),
                        soa.refresh(),
                        soa.retry(),
                        soa.expire(),
                        soa.minimum()
                    )
                }
                RData::TXT(ref txt) => format!(" * TXT: {}", txt.txt_data().join(" ")),
                RData::PTR(ref ptr) => format!(" * PTR: {}", ptr.to_string()),
                ref x => format!(" * unclassified answer: {:?}", x),
            })
            .collect();
        answers.sort();
        write!(f, "{}", answers.join("\n"))
    }
}

error_chain! {
    errors {
        CliArgsParsingError {
            description("failed to parse CLI arguments")
            display("failed to parse CLI arguments")
        }

        ResolvConfError {
            description("failed to parse /etc/resolv.conf")
            display("failed to parse /etc/resolv.cons")
        }

        ServerIpAddrParsingError {
            description("failed to parse server IP address")
            display("failed to parse server IP address")
        }

        ResoureRecordTypeParsingError {
            description("failed to parse resource record type")
            display("failed to parse resource record type")
        }
    }
}

quick_main!(run);
<commit_msg>Clippify<commit_after>#[macro_use]
extern crate clap;
#[macro_use]
extern crate error_chain;
extern crate mhost;
extern crate resolv_conf;
extern crate tokio_core;
extern crate trust_dns;

use mhost::{multiple_lookup, DnsQuery};

use clap::{App, Arg, ArgMatches, Shell};
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::net::IpAddr;
use std::str::FromStr;
use std::time::Duration;
use tokio_core::reactor::Core;
use trust_dns::rr::{RData, RecordType};

// Arbitrary list of public DNS servers
static DEFAULT_DNS_SERVERS: &'static [&str] = &[
    // "Level3",
    "209.244.0.3",
    "209.244.0.4",
    // "Verisign",
    "64.6.64.6",
    "64.6.65.6",
    // "Google",
    "8.8.8.8",
    "8.8.4.4",
    // "DNS.WATCH",
    "84.200.69.80",
    "84.200.70.40",
    // "OpenDNS Home",
    "208.67.222.222",
    "208.67.220.220",
    // "SafeDNS",
    "195.46.39.39",
    "195.46.39.40",
    // "Dyn",
    "216.146.35.35",
    "216.146.36.36",
    // "FreeDNS",
    "37.235.1.174",
    "37.235.1.177",
    // "Alternate DNS",
    "198.101.242.72",
    "23.253.163.53",
];

static DEFAULT_RECORD_TYPES: &'static [&str] = &["a", "aaaa", "mx"];

fn run() -> Result<()> {
    let args = build_cli().get_matches();

    if args.is_present("completions") {
        return generate_completion(&args);
    }

    let dns_endpoints = get_dns_servers(&args).chain_err(|| ErrorKind::ServerIpAddrParsingError)?
        .into_iter()
        .map(|s| (s, 53))
        .collect();

    let timeout = value_t!(args, "timeout", u64).map(Duration::from_secs).unwrap();

    let mut query = match IpAddr::from_str(args.value_of("domain name").unwrap()) {
        Ok(ip) => {
            DnsQuery::from(ip, vec![RecordType::PTR])
        }
        Err(_) => {
            let record_types = get_record_types(&args).chain_err(|| ErrorKind::ResoureRecordTypeParsingError)?;
            DnsQuery::new(args.value_of("domain name").unwrap(), record_types)
        }
    };
    query = query.set_timeout(timeout);

    let mut io_loop = Core::new().unwrap();
    let lookup = multiple_lookup(&io_loop.handle(), query, dns_endpoints);
    let result = io_loop.run(lookup);

    match result {
        Ok(ref responses) => {
            for response in responses {
                match *response {
                    Ok(ref x) => println!("{}", DnsResponse(x)),
                    Err(ref e) => println!("Error: {}", e),
                }
            }
        }
        Err(_) => {
            println!("General Error");
        }
    }

    Ok(())
}

fn build_cli() -> App<'static, 'static> {
    let name = env!("CARGO_PKG_NAME");
    let version = env!("CARGO_PKG_VERSION");
    let about = env!("CARGO_PKG_DESCRIPTION");

    App::new(name)
        .version(version)
        .about(about)
        .arg(Arg::with_name("domain name")
            .index(1)
            .required(true)
            .conflicts_with("completions")
            .help("domain name or IP address to look up"))
        .arg(Arg::with_name("timeout")
            .long("timeout")
            .default_value("5")
            .help("timeout for server responses in sec")
            .takes_value(true))
        .arg(Arg::with_name("record types")
            .long("type")
            .short("t")
            .takes_value(true)
            .multiple(true)
            .number_of_values(1)
            .possible_values(&["a", "aaaa", "any", "cname", "dnskey", "mx", "ns", "opt", "ptr", "soa", "srv", "txt"])
            .help("Select resource record type [default: a, aaaa, mx]"))
        .arg(Arg::with_name("dont use local dns servers")
            .short("L")
            .help("Do not use local (/etc/resolv.conf) DNS servers"))
        .arg(Arg::with_name("predefined server")
            .short("S")
            .help("use predefined DNS server set"))
        .arg(Arg::with_name("DNS servers")
            .long("server")
            .short("s")
            .takes_value(true)
            .multiple(true)
            .number_of_values(1)
            .help("DNS servers to use; if empty use predefined, public DNS server"))
        .arg(Arg::with_name("completions")
            .long("completions")
            .takes_value(true)
            .hidden(true)
            .possible_values(&["bash", "fish", "zsh"])
            .help("The shell to generate the script for"))
}

fn generate_completion(args: &ArgMatches) -> Result<()> {
    let bin_name = env!("CARGO_PKG_NAME");
    let shell = args.value_of("completions").ok_or(
        ErrorKind::CliArgsParsingError,
    )?;
    build_cli().gen_completions_to(
        bin_name,
        shell.parse::<Shell>().unwrap(),
        &mut std::io::stdout(),
    );
    Ok(())
}

fn get_local_dns_servers() -> Result<Vec<IpAddr>> {
    let mut buf = Vec::with_capacity(4096);
    let mut f = File::open("/etc/resolv.conf").chain_err(|| ErrorKind::ResolvConfError)?;
    f.read_to_end(&mut buf).unwrap();
    let cfg = resolv_conf::Config::parse(&buf[..]).chain_err(|| ErrorKind::ResolvConfError)?;
    Ok(cfg.nameservers)
}

fn get_dns_servers(args: &ArgMatches) -> Result<Vec<IpAddr>> {
    let mut dns_servers: Vec<IpAddr> = Vec::new();
    if let Some(servers) = args.values_of_lossy("DNS servers") {
        dns_servers.extend(servers
            .into_iter()
            .map(|server|
                IpAddr::from_str(&server).unwrap()
            )
        );
    }
    if args.is_present("predefined server") {
        dns_servers.extend(DEFAULT_DNS_SERVERS
            .iter()
            .map(|server| IpAddr::from_str(server).unwrap())
        );
    }
    if !args.is_present("dont use local dns servers") {
        dns_servers.extend(get_local_dns_servers()?);
    }

    Ok(dns_servers)
}

fn get_record_types(args: &ArgMatches) -> Result<Vec<RecordType>> {
    let record_types = if let Some(record_types) = args.values_of_lossy("record types") {
        record_types
            .iter()
            .map(|rt| RecordType::from_str(&rt.to_uppercase()).unwrap())
            .collect()
    } else {
        DEFAULT_RECORD_TYPES
            .iter()
            .map(|rt| RecordType::from_str(&rt.to_uppercase()).unwrap())
            .collect()
    };

    Ok(record_types)
}

// Newtype pattern for Display implementation
struct DnsResponse<'a>(pub &'a mhost::DnsResponse);

// Display impl for plain, basic output
impl<'a> fmt::Display for DnsResponse<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let DnsResponse(dns_response) = *self;
        if dns_response.answers.is_empty() {
            return write!(f, "DNS server {} has not records.", dns_response.server);
        }
        let _ = write!(f, "DNS server {} responded with\n", dns_response.server);
        let mut answers: Vec<String> = dns_response
            .answers
            .iter()
            .map(|answer| match *answer.rdata() {
                RData::A(ip) => format!(" * IPv4: {}", ip),
                RData::AAAA(ip) => format!(" * IPv6: {}", ip),
                RData::CNAME(ref name) => format!(" * CNAME: {}", name),
                RData::MX(ref mx) => {
                    format!(
                        " * MX: {} with preference {}",
                        mx.exchange(),
                        mx.preference()
                    )
                }
                RData::NS(ref name) => format!(" * NS: {}", name),
                RData::SOA(ref soa) => {
                    format!(
                        " * SOA: {} {} {} {} {} {} {}",
                        soa.mname(),
                        soa.rname(),
                        soa.serial(),
                        soa.refresh(),
                        soa.retry(),
                        soa.expire(),
                        soa.minimum()
                    )
                }
                RData::TXT(ref txt) => format!(" * TXT: {}", txt.txt_data().join(" ")),
                RData::PTR(ref ptr) => format!(" * PTR: {}", ptr.to_string()),
                ref x => format!(" * unclassified answer: {:?}", x),
            })
            .collect();
        answers.sort();
        write!(f, "{}", answers.join("\n"))
    }
}

error_chain! {
    errors {
        CliArgsParsingError {
            description("failed to parse CLI arguments")
            display("failed to parse CLI arguments")
        }

        ResolvConfError {
            description("failed to parse /etc/resolv.conf")
            display("failed to parse /etc/resolv.cons")
        }

        ServerIpAddrParsingError {
            description("failed to parse server IP address")
            display("failed to parse server IP address")
        }

        ResoureRecordTypeParsingError {
            description("failed to parse resource record type")
            display("failed to parse resource record type")
        }
    }
}

quick_main!(run);
<|endoftext|>