<commit_before>use std::{
    collections::BTreeMap,
    convert::TryInto as _,
    env, fmt, fs,
    path::{Path, PathBuf},
    str::FromStr,
};

use chrono::{Datelike as _, Month, TimeZone as _, Utc};
use glob::glob;
use regex::Regex;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Date {
    year: u32,
    month: u32,
}

impl Date {
    fn months_since(self, other: Date) -> Option<u32> {
        let self_chrono = Utc.ymd(self.year.try_into().unwrap(), self.month, 1);
        let other_chrono = Utc.ymd(other.year.try_into().unwrap(), other.month, 1);
        let duration_since = self_chrono.signed_duration_since(other_chrono);
        let months_since = duration_since.num_days() / 30;
        if months_since < 0 {
            None
        } else {
            Some(months_since.try_into().unwrap())
        }
    }
}

impl fmt::Display for Date {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:04}-{:02}", self.year, self.month)
    }
}

fn make_date_regex() -> Regex {
    Regex::new(
        r"(?x) # insignificant whitespace mode
        (<!--\s*
          date-check:\s*
          (?P<m1>[[:alpha:]]+)\s+
          (?P<y1>\d{4})\s*-->
        )
        |
        (<!--\s*
          date-check\s*-->\s+
          (?P<m2>[[:alpha:]]+)\s+
          (?P<y2>\d{4})\b
        )
    ",
    )
    .unwrap()
}

fn collect_dates_from_file(date_regex: &Regex, text: &str) -> Vec<(usize, Date)> {
    let mut line = 1;
    let mut end_of_last_cap = 0;
    date_regex
        .captures_iter(text)
        .filter_map(|cap| {
            if let (Some(month), Some(year), None, None) | (None, None, Some(month), Some(year)) = (
                cap.name("m1"),
                cap.name("y1"),
                cap.name("m2"),
                cap.name("y2"),
            ) {
                let year = year.as_str().parse().expect("year");
                let month = Month::from_str(month.as_str())
                    .expect("month")
                    .number_from_month();
                Some((cap.get(0).expect("all").range(), Date { year, month }))
            } else {
                None
            }
        })
        .map(|(byte_range, date)| {
            line += text[end_of_last_cap..byte_range.end]
                .chars()
                .filter(|c| *c == '\n')
                .count();
            end_of_last_cap = byte_range.end;
            (line, date)
        })
        .collect()
}

fn collect_dates(paths: impl Iterator<Item = PathBuf>) -> BTreeMap<PathBuf, Vec<(usize, Date)>> {
    let date_regex = make_date_regex();
    let mut data = BTreeMap::new();
    for path in paths {
        let text = fs::read_to_string(&path).unwrap();
        let dates = collect_dates_from_file(&date_regex, &text);
        if !dates.is_empty() {
            data.insert(path, dates);
        }
    }
    data
}

fn filter_dates(
    current_month: Date,
    min_months_since: u32,
    dates_by_file: impl Iterator<Item = (PathBuf, Vec<(usize, Date)>)>,
) -> impl Iterator<Item = (PathBuf, Vec<(usize, Date)>)> {
    dates_by_file
        .map(move |(path, dates)| {
            (
                path,
                dates
                    .into_iter()
                    .filter(|(_, date)| {
                        current_month
                            .months_since(*date)
                            .expect("found date that is after current month")
                            >= min_months_since
                    })
                    .collect::<Vec<_>>(),
            )
        })
        .filter(|(_, dates)| !dates.is_empty())
}

fn main() {
    let root_dir = env::args()
        .nth(1)
        .expect("expect root Markdown directory as CLI argument");
    let root_dir_path = Path::new(&root_dir);
    let glob_pat = format!("{}/**/*.md", root_dir);
    let today_chrono = Utc::today();
    let current_month = Date {
        year: today_chrono.year_ce().1,
        month: today_chrono.month(),
    };

    let dates_by_file = collect_dates(glob(&glob_pat).unwrap().map(Result::unwrap));
    let dates_by_file: BTreeMap<_, _> =
        filter_dates(current_month, 6, dates_by_file.into_iter()).collect();

    if dates_by_file.is_empty() {
        println!("empty");
    } else {
        println!("Date Reference Triage for {}", current_month);
        println!("## Procedure");
        println!();
        println!(
            "Each of these dates should be checked to see if the docs they annotate are \
             up-to-date. Each date should be updated (in the Markdown file where it appears) to \
             use the current month ({current_month}), or removed if the docs it annotates are not \
             expected to fall out of date quickly.",
            current_month = today_chrono.format("%B %Y"),
        );
        println!();
        println!(
            "Please check off each date once a PR to update it (and, if applicable, its \
             surrounding docs) has been merged. Please also mention that you are working on a \
             particular set of dates so duplicate work is avoided."
        );
        println!();
        println!("Finally, once all the dates have been updated, please close this issue.");
        println!();
        println!("## Dates");
        println!();

        for (path, dates) in dates_by_file {
            println!(
                "- [ ] {}",
                path.strip_prefix(&root_dir_path).unwrap().display()
            );
            for (line, date) in dates {
                println!("  - [ ] line {}: {}", line, date);
            }
        }
        println!();
    }
}

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

    #[test]
    fn test_months_since() {
        let date1 = Date {
            year: 2020,
            month: 3,
        };
        let date2 = Date {
            year: 2021,
            month: 1,
        };
        assert_eq!(date2.months_since(date1), Some(10));
    }

    #[test]
    fn test_date_regex() {
        let regex = &make_date_regex();
        assert!(regex.is_match("<!-- date-check: jan 2021 -->"));
        assert!(regex.is_match("<!-- date-check: january 2021 -->"));
        assert!(regex.is_match("<!-- date-check: Jan 2021 -->"));
        assert!(regex.is_match("<!-- date-check: January 2021 -->"));
        assert!(regex.is_match("<!-- date-check --> jan 2021"));
        assert!(regex.is_match("<!-- date-check --> january 2021"));
        assert!(regex.is_match("<!-- date-check --> Jan 2021"));
        assert!(regex.is_match("<!-- date-check --> January 2021"));

        assert!(regex.is_match("<!-- date-check --> jan 2021 "));
        assert!(regex.is_match("<!-- date-check --> jan 2021."));
    }

    #[test]
    fn test_date_regex_fail() {
        let regexes = &make_date_regex();
        assert!(!regexes.is_match("<!-- date-check: jan 221 -->"));
        assert!(!regexes.is_match("<!-- date-check: jan 20221 -->"));
        assert!(!regexes.is_match("<!-- date-check: 01 2021 -->"));
        assert!(!regexes.is_match("<!-- date-check --> jan 221"));
        assert!(!regexes.is_match("<!-- date-check --> jan 20222"));
        assert!(!regexes.is_match("<!-- date-check --> 01 2021"));
    }

    #[test]
    fn test_collect_dates_from_file() {
        let text = r"
Test1
<!-- date-check: jan 2021 -->
Test2
Foo<!-- date-check: february 2021
-->
Test3
Test4
Foo<!-- date-check: Mar 2021 -->Bar
<!-- date-check:April 2021
-->
Test5
Test6
Test7
<!-- date-check:

may 2021 -->
Test8
Test1
<!-- date-check -->  jan 2021
Test2
Foo<!-- date-check
--> february 2021
Test3
Test4
Foo<!-- date-check -->  mar 2021 Bar
<!-- date-check
--> apr 2021
Test5
Test6
Test7
<!-- date-check

 --> may 2021
Test8
 <!--
   date-check
 --> june 2021.
        ";
        assert_eq!(
            collect_dates_from_file(&make_date_regex(), text),
            vec![
                (
                    3,
                    Date {
                        year: 2021,
                        month: 1,
                    }
                ),
                (
                    6,
                    Date {
                        year: 2021,
                        month: 2,
                    }
                ),
                (
                    9,
                    Date {
                        year: 2021,
                        month: 3,
                    }
                ),
                (
                    11,
                    Date {
                        year: 2021,
                        month: 4,
                    }
                ),
                (
                    17,
                    Date {
                        year: 2021,
                        month: 5,
                    }
                ),
                (
                    20,
                    Date {
                        year: 2021,
                        month: 1,
                    }
                ),
                (
                    23,
                    Date {
                        year: 2021,
                        month: 2,
                    }
                ),
                (
                    26,
                    Date {
                        year: 2021,
                        month: 3,
                    }
                ),
                (
                    28,
                    Date {
                        year: 2021,
                        month: 4,
                    }
                ),
                (
                    34,
                    Date {
                        year: 2021,
                        month: 5,
                    }
                ),
                (
                    38,
                    Date {
                        year: 2021,
                        month: 6,
                    }
                ),
            ],
        );
    }
}
<commit_msg>make date-check more easy to use<commit_after>use std::{
    collections::BTreeMap,
    convert::TryInto as _,
    env, fmt, fs,
    path::{Path, PathBuf},
    str::FromStr,
};

use chrono::{Datelike as _, Month, TimeZone as _, Utc};
use glob::glob;
use regex::Regex;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Date {
    year: u32,
    month: u32,
}

impl Date {
    fn months_since(self, other: Date) -> Option<u32> {
        let self_chrono = Utc.ymd(self.year.try_into().unwrap(), self.month, 1);
        let other_chrono = Utc.ymd(other.year.try_into().unwrap(), other.month, 1);
        let duration_since = self_chrono.signed_duration_since(other_chrono);
        let months_since = duration_since.num_days() / 30;
        if months_since < 0 {
            None
        } else {
            Some(months_since.try_into().unwrap())
        }
    }
}

impl fmt::Display for Date {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:04}-{:02}", self.year, self.month)
    }
}

fn make_date_regex() -> Regex {
    Regex::new(
        r"(?x) # insignificant whitespace mode
        (<!--\s*
          date-check:\s*
          (?P<m1>[[:alpha:]]+)\s+
          (?P<y1>\d{4})\s*-->
        )
        |
        (<!--\s*
          date-check\s*-->\s+
          (?P<m2>[[:alpha:]]+)\s+
          (?P<y2>\d{4})\b
        )
    ",
    )
    .unwrap()
}

fn collect_dates_from_file(date_regex: &Regex, text: &str) -> Vec<(usize, Date)> {
    let mut line = 1;
    let mut end_of_last_cap = 0;
    date_regex
        .captures_iter(text)
        .filter_map(|cap| {
            if let (Some(month), Some(year), None, None) | (None, None, Some(month), Some(year)) = (
                cap.name("m1"),
                cap.name("y1"),
                cap.name("m2"),
                cap.name("y2"),
            ) {
                let year = year.as_str().parse().expect("year");
                let month = Month::from_str(month.as_str())
                    .expect("month")
                    .number_from_month();
                Some((cap.get(0).expect("all").range(), Date { year, month }))
            } else {
                None
            }
        })
        .map(|(byte_range, date)| {
            line += text[end_of_last_cap..byte_range.end]
                .chars()
                .filter(|c| *c == '\n')
                .count();
            end_of_last_cap = byte_range.end;
            (line, date)
        })
        .collect()
}

fn collect_dates(paths: impl Iterator<Item = PathBuf>) -> BTreeMap<PathBuf, Vec<(usize, Date)>> {
    let date_regex = make_date_regex();
    let mut data = BTreeMap::new();
    for path in paths {
        let text = fs::read_to_string(&path).unwrap();
        let dates = collect_dates_from_file(&date_regex, &text);
        if !dates.is_empty() {
            data.insert(path, dates);
        }
    }
    data
}

fn filter_dates(
    current_month: Date,
    min_months_since: u32,
    dates_by_file: impl Iterator<Item = (PathBuf, Vec<(usize, Date)>)>,
) -> impl Iterator<Item = (PathBuf, Vec<(usize, Date)>)> {
    dates_by_file
        .map(move |(path, dates)| {
            (
                path,
                dates
                    .into_iter()
                    .filter(|(_, date)| {
                        current_month
                            .months_since(*date)
                            .expect("found date that is after current month")
                            >= min_months_since
                    })
                    .collect::<Vec<_>>(),
            )
        })
        .filter(|(_, dates)| !dates.is_empty())
}

fn main() {
    let root_dir = env::args().nth(1).unwrap_or(".".into());
    let root_dir_path = Path::new(&root_dir);
    let glob_pat = format!("{}/**/*.md", root_dir);
    let today_chrono = Utc::today();
    let current_month = Date {
        year: today_chrono.year_ce().1,
        month: today_chrono.month(),
    };

    let dates_by_file = collect_dates(glob(&glob_pat).unwrap().map(Result::unwrap));
    let dates_by_file: BTreeMap<_, _> =
        filter_dates(current_month, 6, dates_by_file.into_iter()).collect();

    if dates_by_file.is_empty() {
        println!("empty");
    } else {
        println!("Date Reference Triage for {}", current_month);
        println!("## Procedure");
        println!();
        println!(
            "Each of these dates should be checked to see if the docs they annotate are \
             up-to-date. Each date should be updated (in the Markdown file where it appears) to \
             use the current month ({current_month}), or removed if the docs it annotates are not \
             expected to fall out of date quickly.",
            current_month = today_chrono.format("%B %Y"),
        );
        println!();
        println!(
            "Please check off each date once a PR to update it (and, if applicable, its \
             surrounding docs) has been merged. Please also mention that you are working on a \
             particular set of dates so duplicate work is avoided."
        );
        println!();
        println!("Finally, once all the dates have been updated, please close this issue.");
        println!();
        println!("## Dates");
        println!();

        for (path, dates) in dates_by_file {
            println!(
                "- [ ] {}",
                path.strip_prefix(&root_dir_path).unwrap_or(&path).display(),
            );
            for (line, date) in dates {
                println!("  - [ ] line {}: {}", line, date);
            }
        }
        println!();
    }
}

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

    #[test]
    fn test_months_since() {
        let date1 = Date {
            year: 2020,
            month: 3,
        };
        let date2 = Date {
            year: 2021,
            month: 1,
        };
        assert_eq!(date2.months_since(date1), Some(10));
    }

    #[test]
    fn test_date_regex() {
        let regex = &make_date_regex();
        assert!(regex.is_match("<!-- date-check: jan 2021 -->"));
        assert!(regex.is_match("<!-- date-check: january 2021 -->"));
        assert!(regex.is_match("<!-- date-check: Jan 2021 -->"));
        assert!(regex.is_match("<!-- date-check: January 2021 -->"));
        assert!(regex.is_match("<!-- date-check --> jan 2021"));
        assert!(regex.is_match("<!-- date-check --> january 2021"));
        assert!(regex.is_match("<!-- date-check --> Jan 2021"));
        assert!(regex.is_match("<!-- date-check --> January 2021"));

        assert!(regex.is_match("<!-- date-check --> jan 2021 "));
        assert!(regex.is_match("<!-- date-check --> jan 2021."));
    }

    #[test]
    fn test_date_regex_fail() {
        let regexes = &make_date_regex();
        assert!(!regexes.is_match("<!-- date-check: jan 221 -->"));
        assert!(!regexes.is_match("<!-- date-check: jan 20221 -->"));
        assert!(!regexes.is_match("<!-- date-check: 01 2021 -->"));
        assert!(!regexes.is_match("<!-- date-check --> jan 221"));
        assert!(!regexes.is_match("<!-- date-check --> jan 20222"));
        assert!(!regexes.is_match("<!-- date-check --> 01 2021"));
    }

    #[test]
    fn test_collect_dates_from_file() {
        let text = r"
Test1
<!-- date-check: jan 2021 -->
Test2
Foo<!-- date-check: february 2021
-->
Test3
Test4
Foo<!-- date-check: Mar 2021 -->Bar
<!-- date-check:April 2021
-->
Test5
Test6
Test7
<!-- date-check:

may 2021 -->
Test8
Test1
<!-- date-check -->  jan 2021
Test2
Foo<!-- date-check
--> february 2021
Test3
Test4
Foo<!-- date-check -->  mar 2021 Bar
<!-- date-check
--> apr 2021
Test5
Test6
Test7
<!-- date-check

 --> may 2021
Test8
 <!--
   date-check
 --> june 2021.
        ";
        assert_eq!(
            collect_dates_from_file(&make_date_regex(), text),
            vec![
                (
                    3,
                    Date {
                        year: 2021,
                        month: 1,
                    }
                ),
                (
                    6,
                    Date {
                        year: 2021,
                        month: 2,
                    }
                ),
                (
                    9,
                    Date {
                        year: 2021,
                        month: 3,
                    }
                ),
                (
                    11,
                    Date {
                        year: 2021,
                        month: 4,
                    }
                ),
                (
                    17,
                    Date {
                        year: 2021,
                        month: 5,
                    }
                ),
                (
                    20,
                    Date {
                        year: 2021,
                        month: 1,
                    }
                ),
                (
                    23,
                    Date {
                        year: 2021,
                        month: 2,
                    }
                ),
                (
                    26,
                    Date {
                        year: 2021,
                        month: 3,
                    }
                ),
                (
                    28,
                    Date {
                        year: 2021,
                        month: 4,
                    }
                ),
                (
                    34,
                    Date {
                        year: 2021,
                        month: 5,
                    }
                ),
                (
                    38,
                    Date {
                        year: 2021,
                        month: 6,
                    }
                ),
            ],
        );
    }
}
<|endoftext|>


<commit_before>use libc::{c_uchar, c_void};
use std::mem::transmute;

use Capstone;
use CCapstone1;

use Messages;
use CMessageFuncs1;

use Dialogs;
use CDialogFuncs1;

use MenuFuncs;
use CMenuFuncs1;

pub struct Service {
    pub service_func: extern "C" fn(data: *const c_uchar) -> *mut c_void,
}

impl Service {
    // TODO: Handle different versions

    pub fn get_messages(&self) -> Messages {
        unsafe {
            let api: &mut CMessageFuncs1 = transmute(((*self).service_func)(b"Info Messages 1\0"
                                                                                .as_ptr()));
            Messages { api: api }
        }
    }

    pub fn get_capstone(&self) -> Capstone {
        unsafe {
            let api: &mut CCapstone1 = transmute(((*self).service_func)(b"Capstone Service 1\0"
                                                                            .as_ptr()));
            Capstone {
                api: api,
                handle: ::std::ptr::null(),
            }
        }
    }

    pub fn get_dialogs(&self) -> Dialogs {
        unsafe {
            let api: &mut CDialogFuncs1 = transmute(((*self).service_func)(b"Dialogs 1\0"
                                                                               .as_ptr()));
            Dialogs { api: api }
        }
    }

    pub fn get_menu_service(&self) -> MenuFuncs {
        unsafe {
            let api: &mut CMenuFuncs1 = transmute(((*self).service_func)(b"Menu Service 1\0"
                                                                               .as_ptr()));
            MenuFuncs { api: api }
        }
    }
}
<commit_msg>Removed some unused code<commit_after>use libc::{c_uchar, c_void};
use std::mem::transmute;

use Capstone;
use CCapstone1;

use Messages;
use CMessageFuncs1;

use Dialogs;
use CDialogFuncs1;

pub struct Service {
    pub service_func: extern "C" fn(data: *const c_uchar) -> *mut c_void,
}

impl Service {
    // TODO: Handle different versions

    pub fn get_messages(&self) -> Messages {
        unsafe {
            let api: &mut CMessageFuncs1 = transmute(((*self).service_func)(b"Info Messages 1\0"
                                                                                .as_ptr()));
            Messages { api: api }
        }
    }

    pub fn get_capstone(&self) -> Capstone {
        unsafe {
            let api: &mut CCapstone1 = transmute(((*self).service_func)(b"Capstone Service 1\0"
                                                                            .as_ptr()));
            Capstone {
                api: api,
                handle: ::std::ptr::null(),
            }
        }
    }

    pub fn get_dialogs(&self) -> Dialogs {
        unsafe {
            let api: &mut CDialogFuncs1 = transmute(((*self).service_func)(b"Dialogs 1\0"
                                                                               .as_ptr()));
            Dialogs { api: api }
        }
    }
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::ops::Range;

use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then};
use rustc_ast::ast::{Expr, ExprKind, Mac, StrLit, StrStyle};
use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
use rustc_errors::Applicability;
use rustc_lexer::unescape::{self, EscapeError};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_parse::parser;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Symbol;
use rustc_span::{BytePos, Span};

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `println!("")` to
    /// print a newline.
    ///
    /// **Why is this bad?** You should use `println!()`, which is simpler.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// println!("");
    /// ```
    pub PRINTLN_EMPTY_STRING,
    style,
    "using `println!(\"\")` with an empty string"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `print!()` with a format
    /// string that
    /// ends in a newline.
    ///
    /// **Why is this bad?** You should use `println!()` instead, which appends the
    /// newline.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # let name = "World";
    /// print!("Hello {}!\n", name);
    /// ```
    /// use println!() instead
    /// ```rust
    /// # let name = "World";
    /// println!("Hello {}!", name);
    /// ```
    pub PRINT_WITH_NEWLINE,
    style,
    "using `print!()` with a format string that ends in a single newline"
}

declare_clippy_lint! {
    /// **What it does:** Checks for printing on *stdout*. The purpose of this lint
    /// is to catch debugging remnants.
    ///
    /// **Why is this bad?** People often print on *stdout* while debugging an
    /// application and might forget to remove those prints afterward.
    ///
    /// **Known problems:** Only catches `print!` and `println!` calls.
    ///
    /// **Example:**
    /// ```rust
    /// println!("Hello world!");
    /// ```
    pub PRINT_STDOUT,
    restriction,
    "printing on stdout"
}

declare_clippy_lint! {
    /// **What it does:** Checks for use of `Debug` formatting. The purpose of this
    /// lint is to catch debugging remnants.
    ///
    /// **Why is this bad?** The purpose of the `Debug` trait is to facilitate
    /// debugging Rust code. It should not be used in user-facing output.
    ///
    /// **Example:**
    /// ```rust
    /// # let foo = "bar";
    /// println!("{:?}", foo);
    /// ```
    pub USE_DEBUG,
    restriction,
    "use of `Debug`-based formatting"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns about the use of literals as `print!`/`println!` args.
    ///
    /// **Why is this bad?** Using literals as `println!` args is inefficient
    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
    /// (i.e., just put the literal in the format string)
    ///
    /// **Known problems:** Will also warn with macro calls as arguments that expand to literals
    /// -- e.g., `println!("{}", env!("FOO"))`.
    ///
    /// **Example:**
    /// ```rust
    /// println!("{}", "foo");
    /// ```
    /// use the literal without formatting:
    /// ```rust
    /// println!("foo");
    /// ```
    pub PRINT_LITERAL,
    style,
    "printing a literal with a format string"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `writeln!(buf, "")` to
    /// print a newline.
    ///
    /// **Why is this bad?** You should use `writeln!(buf)`, which is simpler.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # use std::fmt::Write;
    /// # let mut buf = String::new();
    /// writeln!(buf, "");
    /// ```
    pub WRITELN_EMPTY_STRING,
    style,
    "using `writeln!(buf, \"\")` with an empty string"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `write!()` with a format
    /// string that
    /// ends in a newline.
    ///
    /// **Why is this bad?** You should use `writeln!()` instead, which appends the
    /// newline.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # use std::fmt::Write;
    /// # let mut buf = String::new();
    /// # let name = "World";
    /// write!(buf, "Hello {}!\n", name);
    /// ```
    pub WRITE_WITH_NEWLINE,
    style,
    "using `write!()` with a format string that ends in a single newline"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args.
    ///
    /// **Why is this bad?** Using literals as `writeln!` args is inefficient
    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
    /// (i.e., just put the literal in the format string)
    ///
    /// **Known problems:** Will also warn with macro calls as arguments that expand to literals
    /// -- e.g., `writeln!(buf, "{}", env!("FOO"))`.
    ///
    /// **Example:**
    /// ```rust
    /// # use std::fmt::Write;
    /// # let mut buf = String::new();
    /// writeln!(buf, "{}", "foo");
    /// ```
    pub WRITE_LITERAL,
    style,
    "writing a literal with a format string"
}

declare_lint_pass!(Write => [
    PRINT_WITH_NEWLINE,
    PRINTLN_EMPTY_STRING,
    PRINT_STDOUT,
    USE_DEBUG,
    PRINT_LITERAL,
    WRITE_WITH_NEWLINE,
    WRITELN_EMPTY_STRING,
    WRITE_LITERAL
]);

impl EarlyLintPass for Write {
    fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &Mac) {
        if mac.path == sym!(println) {
            span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
            if let (Some(fmt_str), _) = check_tts(cx, &mac.args.inner_tokens(), false) {
                if fmt_str.symbol == Symbol::intern("") {
                    span_lint_and_sugg(
                        cx,
                        PRINTLN_EMPTY_STRING,
                        mac.span(),
                        "using `println!(\"\")`",
                        "replace it with",
                        "println!()".to_string(),
                        Applicability::MachineApplicable,
                    );
                }
            }
        } else if mac.path == sym!(print) {
            span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`");
            if let (Some(fmt_str), _) = check_tts(cx, &mac.args.inner_tokens(), false) {
                if check_newlines(&fmt_str) {
                    span_lint_and_then(
                        cx,
                        PRINT_WITH_NEWLINE,
                        mac.span(),
                        "using `print!()` with a format string that ends in a single newline",
                        |err| {
                            err.multipart_suggestion(
                                "use `println!` instead",
                                vec![
                                    (mac.path.span, String::from("println")),
                                    (newline_span(&fmt_str), String::new()),
                                ],
                                Applicability::MachineApplicable,
                            );
                        },
                    );
                }
            }
        } else if mac.path == sym!(write) {
            if let (Some(fmt_str), _) = check_tts(cx, &mac.args.inner_tokens(), true) {
                if check_newlines(&fmt_str) {
                    span_lint_and_then(
                        cx,
                        WRITE_WITH_NEWLINE,
                        mac.span(),
                        "using `write!()` with a format string that ends in a single newline",
                        |err| {
                            err.multipart_suggestion(
                                "use `writeln!()` instead",
                                vec![
                                    (mac.path.span, String::from("writeln")),
                                    (newline_span(&fmt_str), String::new()),
                                ],
                                Applicability::MachineApplicable,
                            );
                        },
                    )
                }
            }
        } else if mac.path == sym!(writeln) {
            if let (Some(fmt_str), expr) = check_tts(cx, &mac.args.inner_tokens(), true) {
                if fmt_str.symbol == Symbol::intern("") {
                    let mut applicability = Applicability::MachineApplicable;
                    let suggestion = expr.map_or_else(
                        move || {
                            applicability = Applicability::HasPlaceholders;
                            Cow::Borrowed("v")
                        },
                        move |expr| snippet_with_applicability(cx, expr.span, "v", &mut applicability),
                    );

                    span_lint_and_sugg(
                        cx,
                        WRITELN_EMPTY_STRING,
                        mac.span(),
                        format!("using `writeln!({}, \"\")`", suggestion).as_str(),
                        "replace it with",
                        format!("writeln!({})", suggestion),
                        applicability,
                    );
                }
            }
        }
    }
}

/// Given a format string that ends in a newline and its span, calculates the span of the
/// newline.
fn newline_span(fmtstr: &StrLit) -> Span {
    let sp = fmtstr.span;
    let contents = &fmtstr.symbol.as_str();

    let newline_sp_hi = sp.hi()
        - match fmtstr.style {
            StrStyle::Cooked => BytePos(1),
            StrStyle::Raw(hashes) => BytePos((1 + hashes).into()),
        };

    let newline_sp_len = if contents.ends_with('\n') {
        BytePos(1)
    } else if contents.ends_with(r"\n") {
        BytePos(2)
    } else {
        panic!("expected format string to contain a newline");
    };

    sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi)
}

/// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
/// `Option`s. The first `Option` of the tuple is the macro's format string. It includes
/// the contents of the string, whether it's a raw string, and the span of the literal in the
/// source. The second `Option` in the tuple is, in the `write[ln]!` case, the expression the
/// `format_str` should be written to.
///
/// Example:
///
/// Calling this function on
/// ```rust
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// # let something = "something";
/// writeln!(buf, "string to write: {}", something);
/// ```
/// will return
/// ```rust,ignore
/// (Some("string to write: {}"), Some(buf))
/// ```
#[allow(clippy::too_many_lines)]
fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &TokenStream, is_write: bool) -> (Option<StrLit>, Option<Expr>) {
    use fmt_macros::{
        AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec, Parser, Piece,
    };
    let tts = tts.clone();

    let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, None, false, false, None);
    let mut expr: Option<Expr> = None;
    if is_write {
        expr = match parser.parse_expr().map_err(|mut err| err.cancel()) {
            Ok(p) => Some(p.into_inner()),
            Err(_) => return (None, None),
        };
        // might be `writeln!(foo)`
        if parser.expect(&token::Comma).map_err(|mut err| err.cancel()).is_err() {
            return (None, expr);
        }
    }

    let fmtstr = match parser.parse_str_lit() {
        Ok(fmtstr) => fmtstr,
        Err(_) => return (None, expr),
    };
    let tmp = fmtstr.symbol.as_str();
    let mut args = vec![];
    let mut fmt_parser = Parser::new(&tmp, None, Vec::new(), false);
    while let Some(piece) = fmt_parser.next() {
        if !fmt_parser.errors.is_empty() {
            return (None, expr);
        }
        if let Piece::NextArgument(arg) = piece {
            if arg.format.ty == "?" {
                // FIXME: modify rustc's fmt string parser to give us the current span
                span_lint(cx, USE_DEBUG, parser.prev_span, "use of `Debug`-based formatting");
            }
            args.push(arg);
        }
    }
    let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
    let mut idx = 0;
    loop {
        const SIMPLE: FormatSpec<'_> = FormatSpec {
            fill: None,
            align: AlignUnknown,
            flags: 0,
            precision: CountImplied,
            precision_span: None,
            width: CountImplied,
            width_span: None,
            ty: "",
            ty_span: None,
        };
        if !parser.eat(&token::Comma) {
            return (Some(fmtstr), expr);
        }
        let token_expr = if let Ok(expr) = parser.parse_expr().map_err(|mut err| err.cancel()) {
            expr
        } else {
            return (Some(fmtstr), None);
        };
        match &token_expr.kind {
            ExprKind::Lit(_) => {
                let mut all_simple = true;
                let mut seen = false;
                for arg in &args {
                    match arg.position {
                        ArgumentImplicitlyIs(n) | ArgumentIs(n) => {
                            if n == idx {
                                all_simple &= arg.format == SIMPLE;
                                seen = true;
                            }
                        },
                        ArgumentNamed(_) => {},
                    }
                }
                if all_simple && seen {
                    span_lint(cx, lint, token_expr.span, "literal with an empty format string");
                }
                idx += 1;
            },
            ExprKind::Assign(lhs, rhs, _) => {
                if let ExprKind::Lit(_) = rhs.kind {
                    if let ExprKind::Path(_, p) = &lhs.kind {
                        let mut all_simple = true;
                        let mut seen = false;
                        for arg in &args {
                            match arg.position {
                                ArgumentImplicitlyIs(_) | ArgumentIs(_) => {},
                                ArgumentNamed(name) => {
                                    if *p == name {
                                        seen = true;
                                        all_simple &= arg.format == SIMPLE;
                                    }
                                },
                            }
                        }
                        if all_simple && seen {
                            span_lint(cx, lint, rhs.span, "literal with an empty format string");
                        }
                    }
                }
            },
            _ => idx += 1,
        }
    }
}

/// Checks if the format string contains a single newline that terminates it.
///
/// Literal and escaped newlines are both checked (only literal for raw strings).
fn check_newlines(fmtstr: &StrLit) -> bool {
    let mut has_internal_newline = false;
    let mut last_was_cr = false;
    let mut should_lint = false;

    let contents = &fmtstr.symbol.as_str();

    let mut cb = |r: Range<usize>, c: Result<char, EscapeError>| {
        let c = c.unwrap();

        if r.end == contents.len() && c == '\n' && !last_was_cr && !has_internal_newline {
            should_lint = true;
        } else {
            last_was_cr = c == '\r';
            if c == '\n' {
                has_internal_newline = true;
            }
        }
    };

    match fmtstr.style {
        StrStyle::Cooked => unescape::unescape_str(contents, &mut cb),
        StrStyle::Raw(_) => unescape::unescape_raw_str(contents, &mut cb),
    }

    should_lint
}
<commit_msg>Rustup to rust-lang/rust#69579<commit_after>use std::borrow::Cow;
use std::ops::Range;

use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then};
use rustc_ast::ast::{Expr, ExprKind, Mac, StrLit, StrStyle};
use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
use rustc_errors::Applicability;
use rustc_lexer::unescape::{self, EscapeError};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_parse::parser;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Symbol;
use rustc_span::{BytePos, Span};

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `println!("")` to
    /// print a newline.
    ///
    /// **Why is this bad?** You should use `println!()`, which is simpler.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// println!("");
    /// ```
    pub PRINTLN_EMPTY_STRING,
    style,
    "using `println!(\"\")` with an empty string"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `print!()` with a format
    /// string that
    /// ends in a newline.
    ///
    /// **Why is this bad?** You should use `println!()` instead, which appends the
    /// newline.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # let name = "World";
    /// print!("Hello {}!\n", name);
    /// ```
    /// use println!() instead
    /// ```rust
    /// # let name = "World";
    /// println!("Hello {}!", name);
    /// ```
    pub PRINT_WITH_NEWLINE,
    style,
    "using `print!()` with a format string that ends in a single newline"
}

declare_clippy_lint! {
    /// **What it does:** Checks for printing on *stdout*. The purpose of this lint
    /// is to catch debugging remnants.
    ///
    /// **Why is this bad?** People often print on *stdout* while debugging an
    /// application and might forget to remove those prints afterward.
    ///
    /// **Known problems:** Only catches `print!` and `println!` calls.
    ///
    /// **Example:**
    /// ```rust
    /// println!("Hello world!");
    /// ```
    pub PRINT_STDOUT,
    restriction,
    "printing on stdout"
}

declare_clippy_lint! {
    /// **What it does:** Checks for use of `Debug` formatting. The purpose of this
    /// lint is to catch debugging remnants.
    ///
    /// **Why is this bad?** The purpose of the `Debug` trait is to facilitate
    /// debugging Rust code. It should not be used in user-facing output.
    ///
    /// **Example:**
    /// ```rust
    /// # let foo = "bar";
    /// println!("{:?}", foo);
    /// ```
    pub USE_DEBUG,
    restriction,
    "use of `Debug`-based formatting"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns about the use of literals as `print!`/`println!` args.
    ///
    /// **Why is this bad?** Using literals as `println!` args is inefficient
    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
    /// (i.e., just put the literal in the format string)
    ///
    /// **Known problems:** Will also warn with macro calls as arguments that expand to literals
    /// -- e.g., `println!("{}", env!("FOO"))`.
    ///
    /// **Example:**
    /// ```rust
    /// println!("{}", "foo");
    /// ```
    /// use the literal without formatting:
    /// ```rust
    /// println!("foo");
    /// ```
    pub PRINT_LITERAL,
    style,
    "printing a literal with a format string"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `writeln!(buf, "")` to
    /// print a newline.
    ///
    /// **Why is this bad?** You should use `writeln!(buf)`, which is simpler.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # use std::fmt::Write;
    /// # let mut buf = String::new();
    /// writeln!(buf, "");
    /// ```
    pub WRITELN_EMPTY_STRING,
    style,
    "using `writeln!(buf, \"\")` with an empty string"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `write!()` with a format
    /// string that
    /// ends in a newline.
    ///
    /// **Why is this bad?** You should use `writeln!()` instead, which appends the
    /// newline.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # use std::fmt::Write;
    /// # let mut buf = String::new();
    /// # let name = "World";
    /// write!(buf, "Hello {}!\n", name);
    /// ```
    pub WRITE_WITH_NEWLINE,
    style,
    "using `write!()` with a format string that ends in a single newline"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args.
    ///
    /// **Why is this bad?** Using literals as `writeln!` args is inefficient
    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
    /// (i.e., just put the literal in the format string)
    ///
    /// **Known problems:** Will also warn with macro calls as arguments that expand to literals
    /// -- e.g., `writeln!(buf, "{}", env!("FOO"))`.
    ///
    /// **Example:**
    /// ```rust
    /// # use std::fmt::Write;
    /// # let mut buf = String::new();
    /// writeln!(buf, "{}", "foo");
    /// ```
    pub WRITE_LITERAL,
    style,
    "writing a literal with a format string"
}

declare_lint_pass!(Write => [
    PRINT_WITH_NEWLINE,
    PRINTLN_EMPTY_STRING,
    PRINT_STDOUT,
    USE_DEBUG,
    PRINT_LITERAL,
    WRITE_WITH_NEWLINE,
    WRITELN_EMPTY_STRING,
    WRITE_LITERAL
]);

impl EarlyLintPass for Write {
    fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &Mac) {
        if mac.path == sym!(println) {
            span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
            if let (Some(fmt_str), _) = check_tts(cx, &mac.args.inner_tokens(), false) {
                if fmt_str.symbol == Symbol::intern("") {
                    span_lint_and_sugg(
                        cx,
                        PRINTLN_EMPTY_STRING,
                        mac.span(),
                        "using `println!(\"\")`",
                        "replace it with",
                        "println!()".to_string(),
                        Applicability::MachineApplicable,
                    );
                }
            }
        } else if mac.path == sym!(print) {
            span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`");
            if let (Some(fmt_str), _) = check_tts(cx, &mac.args.inner_tokens(), false) {
                if check_newlines(&fmt_str) {
                    span_lint_and_then(
                        cx,
                        PRINT_WITH_NEWLINE,
                        mac.span(),
                        "using `print!()` with a format string that ends in a single newline",
                        |err| {
                            err.multipart_suggestion(
                                "use `println!` instead",
                                vec![
                                    (mac.path.span, String::from("println")),
                                    (newline_span(&fmt_str), String::new()),
                                ],
                                Applicability::MachineApplicable,
                            );
                        },
                    );
                }
            }
        } else if mac.path == sym!(write) {
            if let (Some(fmt_str), _) = check_tts(cx, &mac.args.inner_tokens(), true) {
                if check_newlines(&fmt_str) {
                    span_lint_and_then(
                        cx,
                        WRITE_WITH_NEWLINE,
                        mac.span(),
                        "using `write!()` with a format string that ends in a single newline",
                        |err| {
                            err.multipart_suggestion(
                                "use `writeln!()` instead",
                                vec![
                                    (mac.path.span, String::from("writeln")),
                                    (newline_span(&fmt_str), String::new()),
                                ],
                                Applicability::MachineApplicable,
                            );
                        },
                    )
                }
            }
        } else if mac.path == sym!(writeln) {
            if let (Some(fmt_str), expr) = check_tts(cx, &mac.args.inner_tokens(), true) {
                if fmt_str.symbol == Symbol::intern("") {
                    let mut applicability = Applicability::MachineApplicable;
                    let suggestion = expr.map_or_else(
                        move || {
                            applicability = Applicability::HasPlaceholders;
                            Cow::Borrowed("v")
                        },
                        move |expr| snippet_with_applicability(cx, expr.span, "v", &mut applicability),
                    );

                    span_lint_and_sugg(
                        cx,
                        WRITELN_EMPTY_STRING,
                        mac.span(),
                        format!("using `writeln!({}, \"\")`", suggestion).as_str(),
                        "replace it with",
                        format!("writeln!({})", suggestion),
                        applicability,
                    );
                }
            }
        }
    }
}

/// Given a format string that ends in a newline and its span, calculates the span of the
/// newline.
fn newline_span(fmtstr: &StrLit) -> Span {
    let sp = fmtstr.span;
    let contents = &fmtstr.symbol.as_str();

    let newline_sp_hi = sp.hi()
        - match fmtstr.style {
            StrStyle::Cooked => BytePos(1),
            StrStyle::Raw(hashes) => BytePos((1 + hashes).into()),
        };

    let newline_sp_len = if contents.ends_with('\n') {
        BytePos(1)
    } else if contents.ends_with(r"\n") {
        BytePos(2)
    } else {
        panic!("expected format string to contain a newline");
    };

    sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi)
}

/// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
/// `Option`s. The first `Option` of the tuple is the macro's format string. It includes
/// the contents of the string, whether it's a raw string, and the span of the literal in the
/// source. The second `Option` in the tuple is, in the `write[ln]!` case, the expression the
/// `format_str` should be written to.
///
/// Example:
///
/// Calling this function on
/// ```rust
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// # let something = "something";
/// writeln!(buf, "string to write: {}", something);
/// ```
/// will return
/// ```rust,ignore
/// (Some("string to write: {}"), Some(buf))
/// ```
#[allow(clippy::too_many_lines)]
fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &TokenStream, is_write: bool) -> (Option<StrLit>, Option<Expr>) {
    use fmt_macros::{
        AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec, Parser, Piece,
    };
    let tts = tts.clone();

    let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, None, false, false, None);
    let mut expr: Option<Expr> = None;
    if is_write {
        expr = match parser.parse_expr().map_err(|mut err| err.cancel()) {
            Ok(p) => Some(p.into_inner()),
            Err(_) => return (None, None),
        };
        // might be `writeln!(foo)`
        if parser.expect(&token::Comma).map_err(|mut err| err.cancel()).is_err() {
            return (None, expr);
        }
    }

    let fmtstr = match parser.parse_str_lit() {
        Ok(fmtstr) => fmtstr,
        Err(_) => return (None, expr),
    };
    let tmp = fmtstr.symbol.as_str();
    let mut args = vec![];
    let mut fmt_parser = Parser::new(&tmp, None, Vec::new(), false);
    while let Some(piece) = fmt_parser.next() {
        if !fmt_parser.errors.is_empty() {
            return (None, expr);
        }
        if let Piece::NextArgument(arg) = piece {
            if arg.format.ty == "?" {
                // FIXME: modify rustc's fmt string parser to give us the current span
                span_lint(cx, USE_DEBUG, parser.prev_token.span, "use of `Debug`-based formatting");
            }
            args.push(arg);
        }
    }
    let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
    let mut idx = 0;
    loop {
        const SIMPLE: FormatSpec<'_> = FormatSpec {
            fill: None,
            align: AlignUnknown,
            flags: 0,
            precision: CountImplied,
            precision_span: None,
            width: CountImplied,
            width_span: None,
            ty: "",
            ty_span: None,
        };
        if !parser.eat(&token::Comma) {
            return (Some(fmtstr), expr);
        }
        let token_expr = if let Ok(expr) = parser.parse_expr().map_err(|mut err| err.cancel()) {
            expr
        } else {
            return (Some(fmtstr), None);
        };
        match &token_expr.kind {
            ExprKind::Lit(_) => {
                let mut all_simple = true;
                let mut seen = false;
                for arg in &args {
                    match arg.position {
                        ArgumentImplicitlyIs(n) | ArgumentIs(n) => {
                            if n == idx {
                                all_simple &= arg.format == SIMPLE;
                                seen = true;
                            }
                        },
                        ArgumentNamed(_) => {},
                    }
                }
                if all_simple && seen {
                    span_lint(cx, lint, token_expr.span, "literal with an empty format string");
                }
                idx += 1;
            },
            ExprKind::Assign(lhs, rhs, _) => {
                if let ExprKind::Lit(_) = rhs.kind {
                    if let ExprKind::Path(_, p) = &lhs.kind {
                        let mut all_simple = true;
                        let mut seen = false;
                        for arg in &args {
                            match arg.position {
                                ArgumentImplicitlyIs(_) | ArgumentIs(_) => {},
                                ArgumentNamed(name) => {
                                    if *p == name {
                                        seen = true;
                                        all_simple &= arg.format == SIMPLE;
                                    }
                                },
                            }
                        }
                        if all_simple && seen {
                            span_lint(cx, lint, rhs.span, "literal with an empty format string");
                        }
                    }
                }
            },
            _ => idx += 1,
        }
    }
}

/// Checks if the format string contains a single newline that terminates it.
///
/// Literal and escaped newlines are both checked (only literal for raw strings).
fn check_newlines(fmtstr: &StrLit) -> bool {
    let mut has_internal_newline = false;
    let mut last_was_cr = false;
    let mut should_lint = false;

    let contents = &fmtstr.symbol.as_str();

    let mut cb = |r: Range<usize>, c: Result<char, EscapeError>| {
        let c = c.unwrap();

        if r.end == contents.len() && c == '\n' && !last_was_cr && !has_internal_newline {
            should_lint = true;
        } else {
            last_was_cr = c == '\r';
            if c == '\n' {
                has_internal_newline = true;
            }
        }
    };

    match fmtstr.style {
        StrStyle::Cooked => unescape::unescape_str(contents, &mut cb),
        StrStyle::Raw(_) => unescape::unescape_raw_str(contents, &mut cb),
    }

    should_lint
}
<|endoftext|>


<commit_before>use std::net::{TcpListener};
use std::io::{BufReader, BufWriter, BufRead, Write};

fn main() {
    let listener = match TcpListener::bind("127.0.0.1:8888") {
        Ok(v) => { println!("127.0.0.1:8888 binded."); v },
        Err(e) => { panic!("bind failed : {:?}", e) }
    };

    for stream in listener.incoming() {
        let client = match stream {
            Ok(v) => { println!("new client accepted! {:?}", v.peer_addr().unwrap()); v },
                Err(e) => { panic!("accept failed : {:?}", e); }
        };

        let mut reader = BufReader::new(&client);
        let mut writer = BufWriter::new(&client);

        let mut line = String::new();
        loop {
            match reader.read_line(&mut line) {
                Ok(len) => {
                    println!("read : {} chars, {}", len, line);
                    len
                },
                Err(e) => {
                    println!("read error! {:?}", e);
                    break;
                }
            };

            write!(writer, "{}", line);
            writer.flush();

            line.clear();
        }
    }

}
<commit_msg>Rust_TcpEcho: upgrade to support multiple connections<commit_after>use std::net::{TcpListener};
use std::io::{BufReader, BufWriter, BufRead, Write};
use std::thread;

fn main() {
    let listener = match TcpListener::bind("127.0.0.1:8888") {
        Ok(v) => { println!("127.0.0.1:8888 binded."); v },
        Err(e) => { panic!("bind failed : {:?}", e) }
    };

    let mut threads = vec![];

    for stream in listener.incoming() {
        let client = match stream {
            Ok(v) => { println!("new client accepted! {:?}", v.peer_addr().unwrap()); v },
                Err(e) => { panic!("accept failed : {:?}", e); }
        };

        let handle = thread::spawn(move || {
            let mut reader = BufReader::new(&client);
            let mut writer = BufWriter::new(&client);

            let mut line = String::new();
            loop {
                match reader.read_line(&mut line) {
                    Ok(_) => {
                        println!("[{:?}] {}",
                                client.peer_addr().unwrap(),
                                line.trim());
                    },
                    Err(e) => {
                        println!("[LOG][ERROR] read error! {:?}", e);
                        break;
                    }
                };

                write!(writer, "{}", line);
                match writer.flush() {
                    Err(e) => { println!("[LOG][ERROR] flush error. {:?}", e); },
                    _ => {}
                }

                line.clear();
            }

            println!("client disconnected. {:?}", client.peer_addr().unwrap());
        });

        threads.push(handle);
    }

    for each in threads {
        match each.join() {
            Err(e) => { println!("[LOG][ERROR] thread join error. {:?}", e); },
            _ => {}
        }
    }
}
<|endoftext|>


<commit_before>use super::{heap::Pointer, Heap};
use crate::{
    compiler::{ast_to_hir::AstToHir, cst::CstDb, hir::Id},
    database::Database,
    language_server::utils::LspPositionConversion,
    module::Module,
};
use itertools::Itertools;

#[derive(Default, Clone)]
pub struct Tracer {
    log: Vec<TraceEntry>,
    stack: Vec<TraceEntry>,
}
#[derive(Clone)]
pub enum TraceEntry {
    ValueEvaluated {
        id: Id,
        value: Pointer,
    },
    CallStarted {
        id: Id,
        closure: Pointer,
        args: Vec<Pointer>,
    },
    CallEnded {
        return_value: Pointer,
    },
    NeedsStarted {
        id: Id,
        condition: Pointer,
        reason: Pointer,
    },
    NeedsEnded,
    ModuleStarted {
        module: Module,
    },
    ModuleEnded {
        export_map: Pointer,
    },
}

impl Tracer {
    pub fn push(&mut self, entry: TraceEntry) {
        self.log.push(entry.clone());
        match entry {
            TraceEntry::CallStarted { .. } => {
                self.stack.push(entry);
            }
            TraceEntry::CallEnded { .. } => {
                self.stack.pop().unwrap();
            }
            TraceEntry::NeedsStarted { .. } => {
                self.stack.push(entry);
            }
            TraceEntry::NeedsEnded => {
                self.stack.pop().unwrap();
            }
            TraceEntry::ModuleStarted { .. } => {
                self.stack.push(entry);
            }
            TraceEntry::ModuleEnded { .. } => {
                self.stack.pop().unwrap();
            }
            _ => {}
        }
    }
    pub fn log(&self) -> &[TraceEntry] {
        &self.log
    }

    pub fn stack(&self) -> &[TraceEntry] {
        &self.stack
    }
    pub fn dump_stack_trace(&self, db: &Database, heap: &Heap) {
        for line in self.format_stack_trace(db, heap).lines() {
            log::error!("{}", line);
        }
    }
    pub fn format_stack_trace(&self, db: &Database, heap: &Heap) -> String {
        self.stack
            .iter()
            .rev()
            .map(|entry| {
                let (call_string, hir_id) = match entry {
                    TraceEntry::CallStarted { id, closure, args } => (
                        format!(
                            "{closure} {}",
                            args.iter().map(|arg| arg.format(heap)).join(" ")
                        ),
                        Some(id),
                    ),
                    TraceEntry::NeedsStarted {
                        id,
                        condition,
                        reason,
                    } => (
                        format!("needs {} {}", condition.format(heap), reason.format(heap)),
                        Some(id),
                    ),
                    TraceEntry::ModuleStarted { module } => (format!("module {module}"), None),
                    _ => unreachable!(),
                };
                let caller_location_string = {
                    let (hir_id, ast_id, cst_id, span) = if let Some(hir_id) = hir_id {
                        let module = hir_id.module.clone();
                        let ast_id = db.hir_to_ast_id(hir_id.clone());
                        let cst_id = db.hir_to_cst_id(hir_id.clone());
                        let cst = cst_id.map(|id| db.find_cst(module.clone(), id));
                        let span = cst.map(|cst| {
                            (
                                db.offset_to_lsp(module.clone(), cst.span.start),
                                db.offset_to_lsp(module.clone(), cst.span.end),
                            )
                        });
                        (Some(hir_id), ast_id, cst_id, span)
                    } else {
                        (None, None, None, None)
                    };
                    format!(
                        "{}, {}, {}, {}",
                        hir_id
                            .map(|id| format!("{id}"))
                            .unwrap_or_else(|| "<no hir>".to_string()),
                        ast_id
                            .map(|id| format!("{id}"))
                            .unwrap_or_else(|| "<no ast>".to_string()),
                        cst_id
                            .map(|id| format!("{id}"))
                            .unwrap_or_else(|| "<no cst>".to_string()),
                        span.map(|((start_line, start_col), (end_line, end_col))| format!(
                            "{}:{} – {}:{}",
                            start_line, start_col, end_line, end_col
                        ))
                        .unwrap_or_else(|| "<no location>".to_string())
                    )
                };
                format!("{caller_location_string:90} {call_string}")
            })
            .join("\n")
    }

    pub fn format_call_tree(&self, heap: &Heap) -> String {
        let actions = self
            .log
            .iter()
            .map(|entry| match entry {
                TraceEntry::ValueEvaluated { id, value } => {
                    Action::Stay(format!("{id} = {}", value.format(heap)))
                }
                TraceEntry::CallStarted { id, closure, args } => Action::Start(format!(
                    "{id} {} {}",
                    closure.format(heap),
                    args.iter().map(|arg| arg.format(heap)).join(" ")
                )),
                TraceEntry::CallEnded { return_value } => {
                    Action::End(format!(" = {}", return_value.format(heap)))
                }
                TraceEntry::NeedsStarted {
                    id,
                    condition,
                    reason,
                } => Action::Start(format!(
                    "{id} needs {} {}",
                    condition.format(heap),
                    reason.format(heap)
                )),
                TraceEntry::NeedsEnded => Action::End(" = Nothing".to_string()),
                TraceEntry::ModuleStarted { module } => Action::Start(format!("module {module}")),
                TraceEntry::ModuleEnded { export_map } => Action::End(export_map.format(heap)),
            })
            .collect_vec();

        let mut lines = vec![];
        let mut stack = vec![];
        let mut indentation = 0;

        for action in actions {
            let indent = "  ".repeat(indentation);
            match action {
                Action::Start(line) => {
                    stack.push(lines.len());
                    lines.push(format!("{indent}{line}"));
                    indentation += 1;
                }
                Action::End(addendum) => {
                    let start = stack.pop().unwrap();
                    lines[start].push_str(&addendum);
                    indentation -= 1;
                }
                Action::Stay(line) => {
                    lines.push(format!("{indent}{line}"));
                }
            }
        }

        lines.join("\n")
    }
}

enum Action {
    Start(String),
    End(String),
    Stay(String),
}
<commit_msg>Improve tracer output<commit_after>use super::{heap::Pointer, Heap};
use crate::{
    compiler::{ast_to_hir::AstToHir, cst::CstDb, hir::Id},
    database::Database,
    language_server::utils::LspPositionConversion,
    module::Module,
};
use itertools::Itertools;

#[derive(Default, Clone)]
pub struct Tracer {
    log: Vec<TraceEntry>,
    stack: Vec<TraceEntry>,
}
#[derive(Clone)]
pub enum TraceEntry {
    ValueEvaluated {
        id: Id,
        value: Pointer,
    },
    CallStarted {
        id: Id,
        closure: Pointer,
        args: Vec<Pointer>,
    },
    CallEnded {
        return_value: Pointer,
    },
    NeedsStarted {
        id: Id,
        condition: Pointer,
        reason: Pointer,
    },
    NeedsEnded,
    ModuleStarted {
        module: Module,
    },
    ModuleEnded {
        export_map: Pointer,
    },
}

impl Tracer {
    pub fn push(&mut self, entry: TraceEntry) {
        self.log.push(entry.clone());
        match entry {
            TraceEntry::CallStarted { .. } => {
                self.stack.push(entry);
            }
            TraceEntry::CallEnded { .. } => {
                self.stack.pop().unwrap();
            }
            TraceEntry::NeedsStarted { .. } => {
                self.stack.push(entry);
            }
            TraceEntry::NeedsEnded => {
                self.stack.pop().unwrap();
            }
            TraceEntry::ModuleStarted { .. } => {
                self.stack.push(entry);
            }
            TraceEntry::ModuleEnded { .. } => {
                self.stack.pop().unwrap();
            }
            _ => {}
        }
    }
    pub fn log(&self) -> &[TraceEntry] {
        &self.log
    }

    pub fn stack(&self) -> &[TraceEntry] {
        &self.stack
    }
    pub fn dump_stack_trace(&self, db: &Database, heap: &Heap) {
        for line in self.format_stack_trace(db, heap).lines() {
            log::error!("{}", line);
        }
    }
    pub fn format_stack_trace(&self, db: &Database, heap: &Heap) -> String {
        self.stack
            .iter()
            .rev()
            .map(|entry| {
                let (call_string, hir_id) = match entry {
                    TraceEntry::CallStarted { id, closure, args } => (
                        format!(
                            "{closure} {}",
                            args.iter().map(|arg| arg.format(heap)).join(" ")
                        ),
                        Some(id),
                    ),
                    TraceEntry::NeedsStarted {
                        id,
                        condition,
                        reason,
                    } => (
                        format!("needs {} {}", condition.format(heap), reason.format(heap)),
                        Some(id),
                    ),
                    TraceEntry::ModuleStarted { module } => (format!("use {module}"), None),
                    _ => unreachable!(),
                };
                let caller_location_string = {
                    let (hir_id, ast_id, cst_id, span) = if let Some(hir_id) = hir_id {
                        let module = hir_id.module.clone();
                        let ast_id = db.hir_to_ast_id(hir_id.clone());
                        let cst_id = db.hir_to_cst_id(hir_id.clone());
                        let cst = cst_id.map(|id| db.find_cst(module.clone(), id));
                        let span = cst.map(|cst| {
                            (
                                db.offset_to_lsp(module.clone(), cst.span.start),
                                db.offset_to_lsp(module.clone(), cst.span.end),
                            )
                        });
                        (Some(hir_id), ast_id, cst_id, span)
                    } else {
                        (None, None, None, None)
                    };
                    format!(
                        "{}, {}, {}, {}",
                        hir_id
                            .map(|id| format!("{id}"))
                            .unwrap_or_else(|| "<no hir>".to_string()),
                        ast_id
                            .map(|id| format!("{id}"))
                            .unwrap_or_else(|| "<no ast>".to_string()),
                        cst_id
                            .map(|id| format!("{id}"))
                            .unwrap_or_else(|| "<no cst>".to_string()),
                        span.map(|((start_line, start_col), (end_line, end_col))| format!(
                            "{}:{} – {}:{}",
                            start_line, start_col, end_line, end_col
                        ))
                        .unwrap_or_else(|| "<no location>".to_string())
                    )
                };
                format!("{caller_location_string:90} {call_string}")
            })
            .join("\n")
    }

    pub fn format_call_tree(&self, heap: &Heap) -> String {
        let actions = self
            .log
            .iter()
            .map(|entry| match entry {
                TraceEntry::ValueEvaluated { id, value } => {
                    Action::Stay(format!("{id} = {}", value.format(heap)))
                }
                TraceEntry::CallStarted { id, closure, args } => Action::Start(format!(
                    "{id} {} {}",
                    closure.format(heap),
                    args.iter().map(|arg| arg.format(heap)).join(" ")
                )),
                TraceEntry::CallEnded { return_value } => {
                    Action::End(format!(" = {}", return_value.format(heap)))
                }
                TraceEntry::NeedsStarted {
                    id,
                    condition,
                    reason,
                } => Action::Start(format!(
                    "{id} needs {} {}",
                    condition.format(heap),
                    reason.format(heap)
                )),
                TraceEntry::NeedsEnded => Action::End(" = Nothing".to_string()),
                TraceEntry::ModuleStarted { module } => Action::Start(format!("module {module}")),
                TraceEntry::ModuleEnded { export_map } => Action::End(export_map.format(heap)),
            })
            .collect_vec();

        let mut lines = vec![];
        let mut stack = vec![];
        let mut indentation = 0;

        for action in actions {
            let indent = "  ".repeat(indentation);
            match action {
                Action::Start(line) => {
                    stack.push(lines.len());
                    lines.push(format!("{indent}{line}"));
                    indentation += 1;
                }
                Action::End(addendum) => {
                    let start = stack.pop().unwrap();
                    lines[start].push_str(&addendum);
                    indentation -= 1;
                }
                Action::Stay(line) => {
                    lines.push(format!("{indent}{line}"));
                }
            }
        }

        lines.join("\n")
    }
}

enum Action {
    Start(String),
    End(String),
    Stay(String),
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate serde_json;

mod http;
mod nupnp;
mod api;

use adapters::philips_hue::api::structs::*;
use adapters::philips_hue::api::HueLight;
use controller::Controller;
use events::*;
use iron::{ Request, Response, IronResult };
use iron::headers::{ ContentType, AccessControlAllowOrigin };
use iron::status::Status;
use iron::method::Method;
use router::Router;
use service::{ Service, ServiceAdapter, ServiceProperties };
use std::time::Duration;
use std::thread;
use uuid::Uuid;
use std::io::Read;

pub struct PhilipsHueAdapter<T> {
    name: String,
    controller: T,
}

impl<T: Controller> PhilipsHueAdapter<T> {
    pub fn new(controller: T) -> Self {
        debug!("Creating Philips Hue adapter");
        PhilipsHueAdapter { name: "PhilipsHueAdapter".to_owned(),
                       controller: controller,
                     }
    }
}

impl<T: Controller> ServiceAdapter for PhilipsHueAdapter<T> {
    fn get_name(&self) -> String {
        self.name.clone()
    }

    fn start(&self) {
        let mut id = 0;
        let controller = self.controller.clone();

        thread::spawn(move || {
            controller.send_event(
                EventData::AdapterStart {
                    name: "Philips Hue Service Adapter".to_owned()
                }).unwrap();

            let nupnp_hubs = nupnp::query();
            debug!("nUPnP reported Philips Hue bridges: {:?}", nupnp_hubs);

            let available_hubs = nupnp_hubs
                .iter()
                .filter(|hub| hub.is_available())
                .collect::<Vec<_>>();

            let paired_hubs = available_hubs
                .iter()
                .filter(|hub| {
                    info!("Philips Hue bridge {} at {}", hub.id, hub.ip);

                    // Querying the settings fails when not paired with the bridge
                    if hub.is_paired() {
                        true
                    } else {
                        info!("Push pairing button on Philips Hue Bridge ID {}", hub.id);

                        // Try pairing for 120 seconds.
                        for _ in 0..120 {
                            controller.send_event(
                                EventData::AdapterNotification {
                                    info: format!(
                                        "{{\"adapter\": \"philips_hue\", \"message\": \"NeedsPairing\", \"hub\": \"{}\"}}",
                                        hub.id)
                                }).unwrap();
                            if hub.try_pairing() {
                                break;
                            }
                            thread::sleep(Duration::from_millis(1000));
                        }

                        if hub.is_paired() {
                            info!("Paired with Philips Hue Bridge ID {}", hub.id);
                            controller.send_event(
                                EventData::AdapterNotification {
                                    info: format!(
                                         "{{\"adapter\": \"philips_hue\", \"message\": \"PairingSuccess\", \"hub\": \"{}\"}}",
                                         hub.id)
                                 }).unwrap();
                            true
                        } else {
                            warn!("Pairing timeout with Philips Hue Bridge ID {}", hub.id);
                            controller.send_event(
                                EventData::AdapterNotification {
                                    info: format!(
                                         "{{\"adapter\": \"philips_hue\", \"message\": \"PairingTimeout\", \"hub\": \"{}\"}}",
                                         hub.id)
                                 }).unwrap();
                            false
                        }
                    }
                })
                .collect::<Vec<_>>();

            for hub in &paired_hubs {
                hub.is_available();
                // Extract and log some info
                let setting = hub.get_settings();
                let hs = HueHubSettings::new(&setting).unwrap(); // TODO: no unwrap
                info!(
                    "Connected to Philips Hue bridge model {}, ID {}, software version {}, IP address {}",
                    hs.config.modelid, hs.config.bridgeid, hs.config.swversion,
                    hs.config.ipaddress);

                let lights = hub.get_lights();
                for light in lights {
                    debug!("Spawning service for {:?}", light);
                    id += 1;
                    let service = HueLightService::new(controller.clone(), id, light);
                    let service_id = service.get_properties().id;
                    service.start();
                    controller.add_service(Box::new(service));
                    controller.send_event(EventData::ServiceStart { id: service_id }).unwrap();
                }
            }
        });
    }

    fn stop(&self) {
        info!("Stopping Philips Hue adapter");
    }

}

struct HueLightService<T> {
    controller: T,
    properties: ServiceProperties,
    light: HueLight,
}

impl<T: Controller> HueLightService<T> {
    fn new(controller: T, id: u32, light: HueLight) -> Self {
        debug!("Creating HueLightService {} for HueLight {:?}", id, light);
        let service_id = Uuid::new_v4().to_simple_string();
        HueLightService {
            controller: controller.clone(),
            properties: ServiceProperties {
                id: service_id.clone(),
                name: "philips hue service".to_owned(),
                description: "Service for Philips Hue Light".to_owned(),
                http_url: controller.get_http_root_for_service(service_id.clone()),
                ws_url: controller.get_ws_root_for_service(service_id)
            },
            light: light
        }
    }

    fn handle_get_request(&self, cmd: &str) -> IronResult<Response> {
        match cmd {
            "state" => {
                // TODO: Every light.get_*() call produces a
                // get request to the API. Fix requires major
                // internal design change.
                let status = self.light.get_settings();
                let light_state = self.light.get_state();
                let json = format!(
                    "{{\"type\": \"{}\", \"available\": {}, \"on\": {}, \"hue\": {}, \"sat\": {}, \"val\": {}}}",
                    "device/light/colorlight",
                    status.state.reachable, status.state.on,
                    light_state.hue, light_state.sat, light_state.val);
                let mut response = Response::with(json);
                response.status = Some(Status::Ok);
                response.headers.set(ContentType::json());
                response.headers.set(AccessControlAllowOrigin::Any);
                Ok(response)
            },
            _ => {
                error_response()
            }
        }
    }

    fn handle_put_request(&self, cmd: &str, body: String) -> IronResult<Response> {
        match cmd {
            "state" => {
                debug!("Request body for state command: {}", body);
                let state_cmd: Option<StateCmd> = parse_json(&body);
                debug!("Parsed state command: {:?}", state_cmd);
                match state_cmd {
                    Some(value) => {
                        let mut light_state = self.light.get_state();
                        if let Some(on) = value.on { light_state.on = on; }
                        if let Some(hue) = value.hue { light_state.hue = hue; }
                        if let Some(sat) = value.sat { light_state.sat = sat; }
                        if let Some(val) = value.val { light_state.val = val; }
                        self.light.set_state(light_state);
                        success_response()
                    },
                    None => {
                        warn!("Invalid parameters in state command: {}", cmd);
                        error_response()
                    }
                }
            },
            _ => {
                warn!("Invalid command to Hue Light service: {}", cmd);
                error_response()
            }
        }
    }
}

impl<T: Controller> Service for HueLightService<T> {
    fn get_properties(&self) -> ServiceProperties {
        self.properties.clone()
    }

    // Starts the service, it will just spawn a thread and send messages once
    // in a while.
    fn start(&self) {
        self.controller.send_event(
                EventData::ServiceStart { id: self.properties.id.to_string() }).unwrap();
        info!("Service {} started for Philips Hue light \"{}\" on bridge {}",
            self.properties.id, self.light.hue_id, self.light.hub_id);
    }

    fn stop(&self) {
        debug!("Service {} stopped for Philips Hue light \"{}\" on bridge {}",
            self.properties.id, self.light.hue_id, self.light.hub_id);
    }

    // Processes a http request.
    fn process_request(&self, req: &mut Request) -> IronResult<Response> {
        let cmd = req.extensions.get::<Router>().unwrap().find("command").unwrap_or("");
        debug!("Got command {} via {:?}", cmd, req);
        match req.method {
            Method::Get => {
                self.handle_get_request(cmd)
            },
            Method::Put => {
                let mut body = String::new();
                req.body.read_to_string(&mut body).unwrap();
                self.handle_put_request(cmd, body)
            },
            _ => {
                error_response()
            }
        }
    }
}

fn success_response() -> IronResult<Response> {
    let mut response = Response::with("{\"result\": \"success\"}".to_owned());
    response.status = Some(Status::Ok);
    response.headers.set(ContentType::json());
    response.headers.set(AccessControlAllowOrigin::Any);
    Ok(response)
}

fn error_response() ->  IronResult<Response> {
    let mut response = Response::with("{\"result\": \"error\"}".to_owned());
    response.status = Some(Status::MethodNotAllowed);
    response.headers.set(ContentType::json());
    response.headers.set(AccessControlAllowOrigin::Any);
    Ok(response)
}

#[derive(Serialize, Deserialize, Debug)]
struct StateCmd {
    on: Option<bool>,
    hue: Option<f32>,
    sat: Option<f32>,
    val: Option<f32>,
}
<commit_msg>Process the discovered hubs in parallel<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate serde_json;

mod http;
mod nupnp;
mod api;

use adapters::philips_hue::api::structs::*;
use adapters::philips_hue::api::HueLight;
use controller::Controller;
use events::*;
use iron::{ Request, Response, IronResult };
use iron::headers::{ ContentType, AccessControlAllowOrigin };
use iron::status::Status;
use iron::method::Method;
use router::Router;
use service::{ Service, ServiceAdapter, ServiceProperties };
use std::time::Duration;
use std::thread;
use uuid::Uuid;
use std::io::Read;

pub struct PhilipsHueAdapter<T> {
    name: String,
    controller: T,
}

impl<T: Controller> PhilipsHueAdapter<T> {
    pub fn new(controller: T) -> Self {
        debug!("Creating Philips Hue adapter");
        PhilipsHueAdapter { name: "PhilipsHueAdapter".to_owned(),
                       controller: controller,
                     }
    }
}

impl<T: Controller> ServiceAdapter for PhilipsHueAdapter<T> {
    fn get_name(&self) -> String {
        self.name.clone()
    }

    fn start(&self) {
        let mut id = 0;
        let controller = self.controller.clone();

        thread::spawn(move || {
            controller.send_event(
                EventData::AdapterStart {
                    name: "Philips Hue Service Adapter".to_owned()
                }).unwrap();

            let nupnp_hubs = nupnp::query();
            debug!("nUPnP reported Philips Hue bridges: {:?}", nupnp_hubs);

            for hub in nupnp_hubs {
                if !hub.is_available() {
                    continue;
                }

                // For each Hub, spawn a thread that will check availability and
                // pairing.
                let controller = controller.clone();
                thread::spawn(move || {
                    // If the Hub is not paired, try pairing.
                    if !hub.is_paired() {
                        info!("Push pairing button on Philips Hue Bridge ID {}", hub.id);

                        // Try pairing for 120 seconds.
                        for _ in 0..120 {
                            controller.send_event(
                                EventData::AdapterNotification {
                                    info: format!(
                                        "{{\"adapter\": \"philips_hue\", \"message\": \"NeedsPairing\", \"hub\": \"{}\"}}",
                                        hub.id)
                                }).unwrap();
                            if hub.try_pairing() {
                                break;
                            }
                            thread::sleep(Duration::from_millis(1000));
                        }

                        if hub.is_paired() {
                            info!("Paired with Philips Hue Bridge ID {}", hub.id);
                            controller.send_event(
                                EventData::AdapterNotification {
                                    info: format!(
                                         "{{\"adapter\": \"philips_hue\", \"message\": \"PairingSuccess\", \"hub\": \"{}\"}}",
                                         hub.id)
                                 }).unwrap();
                        } else {
                            warn!("Pairing timeout with Philips Hue Bridge ID {}", hub.id);
                            controller.send_event(
                                EventData::AdapterNotification {
                                    info: format!(
                                         "{{\"adapter\": \"philips_hue\", \"message\": \"PairingTimeout\", \"hub\": \"{}\"}}",
                                         hub.id)
                                 }).unwrap();
                            // Giving up for this Hub.
                            return;
                        }
                    }

                    // We have a paired Hub, instanciate the lights services.
                    // Extract and log some info
                    let setting = hub.get_settings();
                    let hs = HueHubSettings::new(&setting).unwrap(); // TODO: no unwrap
                    info!(
                        "Connected to Philips Hue bridge model {}, ID {}, software version {}, IP address {}",
                        hs.config.modelid, hs.config.bridgeid, hs.config.swversion,
                        hs.config.ipaddress);

                    let lights = hub.get_lights();
                    for light in lights {
                        debug!("Creating service for {:?}", light);
                        id += 1;
                        let service = HueLightService::new(controller.clone(), id, light);
                        let service_id = service.get_properties().id;
                        service.start();
                        controller.add_service(Box::new(service));
                        controller.send_event(EventData::ServiceStart { id: service_id }).unwrap();
                    }
                });
            }
        });
    }

    fn stop(&self) {
        info!("Stopping Philips Hue adapter");
    }

}

struct HueLightService<T> {
    controller: T,
    properties: ServiceProperties,
    light: HueLight,
}

impl<T: Controller> HueLightService<T> {
    fn new(controller: T, id: u32, light: HueLight) -> Self {
        debug!("Creating HueLightService {} for HueLight {:?}", id, light);
        let service_id = Uuid::new_v4().to_simple_string();
        HueLightService {
            controller: controller.clone(),
            properties: ServiceProperties {
                id: service_id.clone(),
                name: "philips hue service".to_owned(),
                description: "Service for Philips Hue Light".to_owned(),
                http_url: controller.get_http_root_for_service(service_id.clone()),
                ws_url: controller.get_ws_root_for_service(service_id)
            },
            light: light
        }
    }

    fn handle_get_request(&self, cmd: &str) -> IronResult<Response> {
        match cmd {
            "state" => {
                // TODO: Every light.get_*() call produces a
                // get request to the API. Fix requires major
                // internal design change.
                let status = self.light.get_settings();
                let light_state = self.light.get_state();
                let json = format!(
                    "{{\"type\": \"{}\", \"available\": {}, \"on\": {}, \"hue\": {}, \"sat\": {}, \"val\": {}}}",
                    "device/light/colorlight",
                    status.state.reachable, status.state.on,
                    light_state.hue, light_state.sat, light_state.val);
                let mut response = Response::with(json);
                response.status = Some(Status::Ok);
                response.headers.set(ContentType::json());
                response.headers.set(AccessControlAllowOrigin::Any);
                Ok(response)
            },
            _ => {
                error_response()
            }
        }
    }

    fn handle_put_request(&self, cmd: &str, body: String) -> IronResult<Response> {
        match cmd {
            "state" => {
                debug!("Request body for state command: {}", body);
                let state_cmd: Option<StateCmd> = parse_json(&body);
                debug!("Parsed state command: {:?}", state_cmd);
                match state_cmd {
                    Some(value) => {
                        let mut light_state = self.light.get_state();
                        if let Some(on) = value.on { light_state.on = on; }
                        if let Some(hue) = value.hue { light_state.hue = hue; }
                        if let Some(sat) = value.sat { light_state.sat = sat; }
                        if let Some(val) = value.val { light_state.val = val; }
                        self.light.set_state(light_state);
                        success_response()
                    },
                    None => {
                        warn!("Invalid parameters in state command: {}", cmd);
                        error_response()
                    }
                }
            },
            _ => {
                warn!("Invalid command to Hue Light service: {}", cmd);
                error_response()
            }
        }
    }
}

impl<T: Controller> Service for HueLightService<T> {
    fn get_properties(&self) -> ServiceProperties {
        self.properties.clone()
    }

    // Starts the service, it will just spawn a thread and send messages once
    // in a while.
    fn start(&self) {
        self.controller.send_event(
                EventData::ServiceStart { id: self.properties.id.to_string() }).unwrap();
        info!("Service {} started for Philips Hue light \"{}\" on bridge {}",
            self.properties.id, self.light.hue_id, self.light.hub_id);
    }

    fn stop(&self) {
        debug!("Service {} stopped for Philips Hue light \"{}\" on bridge {}",
            self.properties.id, self.light.hue_id, self.light.hub_id);
    }

    // Processes a http request.
    fn process_request(&self, req: &mut Request) -> IronResult<Response> {
        let cmd = req.extensions.get::<Router>().unwrap().find("command").unwrap_or("");
        debug!("Got command {} via {:?}", cmd, req);
        match req.method {
            Method::Get => {
                self.handle_get_request(cmd)
            },
            Method::Put => {
                let mut body = String::new();
                req.body.read_to_string(&mut body).unwrap();
                self.handle_put_request(cmd, body)
            },
            _ => {
                error_response()
            }
        }
    }
}

fn success_response() -> IronResult<Response> {
    let mut response = Response::with("{\"result\": \"success\"}".to_owned());
    response.status = Some(Status::Ok);
    response.headers.set(ContentType::json());
    response.headers.set(AccessControlAllowOrigin::Any);
    Ok(response)
}

fn error_response() ->  IronResult<Response> {
    let mut response = Response::with("{\"result\": \"error\"}".to_owned());
    response.status = Some(Status::MethodNotAllowed);
    response.headers.set(ContentType::json());
    response.headers.set(AccessControlAllowOrigin::Any);
    Ok(response)
}

#[derive(Serialize, Deserialize, Debug)]
struct StateCmd {
    on: Option<bool>,
    hue: Option<f32>,
    sat: Option<f32>,
    val: Option<f32>,
}
<|endoftext|>