<commit_before>extern crate orbclient;

use orbclient::BmpFile;

use super::{CloneCell, Color, CopyCell, Event, Place, Point, Rect, Renderer, Widget, Window};
use super::callback::Click;

pub struct Menu {
    rect: CopyCell<Rect>,
    text: CloneCell<String>,
    fg: Color,
    entries: Vec<Box<Entry>>,
}

pub struct Action {
    rect: CopyCell<Rect>,
    text: CloneCell<String>,
    icon: Option<BmpFile>,
    bg_up: Color,
    bg_down: Color,
    click_callback: Option<Arc<Fn()>>,
    pressed: CopyCell<bool>,
}

pub struct Separator;

pub trait Entry {
    fn text<S: AsRef<str>>(&mut self) -> S;
}

impl Menu {
    pub fn new(name: &str) -> Self {
        Menu {
            rect: Rect::default(),
            text: CloneCell::new(name.to_owned()),
            fg: Color::rgb(0, 0, 0),
            entries: Vec::with_capacity(10),
        }
    }

    pub fn add_entry<E: Entry>(mut self, entry: E) -> Self {
        self.actions.insert(entry);
        self
    }
}

impl Action {
    pub fn new(text: &str) -> Self {
        Action {
            rect: Rect::default(),
            text: CloneCell::new(name.to_owned()),
            icon: None,
            bg_up: Color::rgb(220, 222, 227),
            bg_down: Color::rgb(203, 205, 210),
            click_callback: None,
            pressed: CopyCell::new(false),
        }
    }

    pub fn add_icon(mut self, icon: BmpFile) -> Self {
        self.icon = Some(icon);
        self
    }
}

impl Widget for Action {
    fn draw(&self, renderer: &mut Renderer, _focused: bool) {
        let rect = self.rect.get();

        if self.pressed.get() {
            renderer.rect(rect, self.bg_down);
        } else {
            renderer.rect(rect, self.bg_up);
        }

        let text = self.text.borrow();

        let mut x = 0;
        let mut y = 0;
        for c in text.chars() {
            if c == '\n' {
                x = 0;
                y += 16;
            } else {
                if x + 8 <= rect.width as i32 && y + 16 <= rect.height as i32 {
                    renderer.char(Point::new(x, y) + rect.point(), c, self.fg);
                }
                x += 8;
            }
        }
    }

    fn event(&self, event: Event, focused: bool, redraw: &mut bool) -> bool {
        match event {
        }
    }
}

impl Entry for Menu {
    fn text<S: AsRef<str>>(&mut self) -> S {
        self.text.get()
    }
}

impl Entry for Action {
    fn text<S: AsRef<str>>(&mut self) -> S {
        self.text.get()
    }
}

impl Entry for Separator {
    fn text<S: AsRef<str>>(&mut self) -> S {
        "separator"
    }
}
<commit_msg>Add more work for `Menu` and `Action`<commit_after>extern crate orbclient;

use orbclient::BmpFile;

use super::{CloneCell, Color, CopyCell, Event, Place, Point, Rect, Renderer, Widget, Window};
use super::callback::Click;

pub struct Menu {
    rect: CopyCell<Rect>,
    text: CloneCell<String>,
    fg: Color,
    entries: Vec<Box<Entry>>,
    activated: CopyCell<bool>,
}

pub struct Action {
    rect: CopyCell<Rect>,
    text: CloneCell<String>,
    icon: Option<BmpFile>,
    bg_up: Color,
    bg_down: Color,
    click_callback: Option<Arc<Fn()>>,
    pressed: CopyCell<bool>,
}

pub struct Separator;

pub trait Entry {
    fn text<S: AsRef<str>>(&mut self) -> S;
}

impl Menu {
    pub fn new(name: &str) -> Self {
        Menu {
            rect: Rect::default(),
            text: CloneCell::new(name.to_owned()),
            fg: Color::rgb(0, 0, 0),
            entries: Vec::with_capacity(10),
            activated: false,
        }
    }

    pub fn add_entry<E: Entry + Place>(mut self, mut entry: E) -> Self {
        {
            let entry_text_len = entry.text().len();
            if self.rect.width < entry_text_len {
                // TODO: consider the icon width and some padding
                self.rect.width = entry_text_len;
            }
        }

        if entry.text() == "separator" {
            self.entries.insert(entry.size(self.rect.width, 10));
        } else {
            self.entries.insert(entry.size(self.rect.width, 30));
        }

        self
    }
}

impl Widget for Menu {
    fn draw(&self, renderer: &mut Renderer, _focused: bool) {
        let rect = self.rect.get();

        if self.activated.get() {
            renderer.rect(rect, self.fg);
        }
    }
}

impl Action {
    pub fn new(text: &str) -> Self {
        Action {
            rect: Rect::default(),
            text: CloneCell::new(name.to_owned()),
            icon: None,
            bg_up: Color::rgb(220, 222, 227),
            bg_down: Color::rgb(203, 205, 210),
            click_callback: None,
            pressed: CopyCell::new(false),
        }
    }

    pub fn add_icon(mut self, icon: BmpFile) -> Self {
        self.icon = Some(icon);
        self
    }
}

impl Place for Action {
    fn position(self, x: i32, y: i32) -> Self {
        let mut rect = self.rect.get();
        rect.x = x;
        rect.y = y;
        self.rect.set(rect);

        self
    }

    fn size(self, width: u32, height: u32) -> Self {
        let mut rect = self.rect.get();
        rect.width = width;
        rect.height = height;
        self.rect.set(rect);

        self
    }
}

impl Widget for Action {
    fn draw(&self, renderer: &mut Renderer, _focused: bool) {
        let rect = self.rect.get();

        if self.pressed.get() {
            renderer.rect(rect, self.bg_down);
        } else {
            renderer.rect(rect, self.bg_up);
        }

        //let text = self.text.borrow();

        //let mut x = 0;
        //let mut y = 0;
        //for c in text.chars() {
        //    if c == '\n' {
        //        x = 0;
        //        y += 16;
        //    } else {
        //        if x + 8 <= rect.width as i32 && y + 16 <= rect.height as i32 {
        //            renderer.char(Point::new(x, y) + rect.point(), c, self.fg);
        //        }
        //        x += 8;
        //    }
        //}
    }

    fn event(&self, event: Event, focused: bool, redraw: &mut bool) -> bool {
        match event {
            Event::Mouse { point, left_button, .. } => {
                let mut click = false;
                let rect = self.rect.get();

                if rect.contains(point) {
                    if left_button && self.pressed.check_set(true) {
                        *redraw = true;
                    } else if self.pressed.check_set(false) {
                        *click = true;
                        redraw = true;
                    }
                } else if !left_button && self.pressed.check_set(false) {
                    *redraw = true;
                }

                if click {
                    let click_point: Point = point - rect.point();
                    self.emit_click(click_point);
                }
            }
        }
    }
}

impl Entry for Menu {
    fn text<S: AsRef<str>>(&mut self) -> S {
        self.text.get()
    }
}

impl Entry for Action {
    fn text<S: AsRef<str>>(&mut self) -> S {
        self.text.get()
    }
}

impl Entry for Separator {
    fn text<S: AsRef<str>>(&mut self) -> S {
        "separator"
    }
}
<|endoftext|>


<commit_before>#![macro_use]

use std::{
    cell::RefCell,
    collections::{HashMap, HashSet},
    fmt,
    string::String,
};

#[derive(PartialEq, Eq, Clone, Copy, Hash)]
pub struct Name {
    id: usize,
}

pub struct Spelling {
    // No two different variables have this the same. Tomatoes may have been added:
    unique: String,
    // The original spelling that the programmer chose.
    orig: String,
}

thread_local! {
    // From `Spelling.unique` to `id`s:
    static id_map: RefCell<HashMap<String, usize>> = RefCell::new(HashMap::new());
    // From `id`s to `Spelling`s
    static spellings: RefCell<Vec<Spelling>> = RefCell::new(vec![]);

    static printables: RefCell<HashMap<usize, String>> = RefCell::new(HashMap::new());
    // The values of `printables`, for lookup purposes.
    static printables_used: RefCell<HashSet<String>> = RefCell::new(HashSet::new());

    // Should we do "naive" freshening for testing purposes?
    static fake_freshness: RefCell<bool> = RefCell::new(false);
}

impl ::runtime::reify::Reifiable for Name {
    fn ty_name() -> Name { n("Name") }

    fn reify(&self) -> ::runtime::eval::Value {
        ::runtime::eval::Value::AbstractSyntax(::ast::Ast::Atom(*self))
    }

    fn reflect(v: &::runtime::eval::Value) -> Name {
        extract!((v) ::runtime::eval::Value::AbstractSyntax = (ref ast)
                          => ::core_forms::ast_to_name(ast))
    }
}

impl std::cmp::PartialOrd for Name {
    fn partial_cmp(&self, other: &Name) -> Option<std::cmp::Ordering> {
        Some(self.orig_sp().cmp(&other.orig_sp()))
    }
}

impl std::cmp::Ord for Name {
    fn cmp(&self, other: &Name) -> std::cmp::Ordering { self.orig_sp().cmp(&other.orig_sp()) }
}

// These are for isolating tests of alpha-equivalence from each other.

pub fn enable_fake_freshness(ff: bool) {
    use std::borrow::BorrowMut;

    fake_freshness.with(|fake_freshness_| {
        *fake_freshness_.borrow_mut() = ff;
    })
}

// only available on nightly:
// impl !Send for Name {}

impl Name {
    pub fn sp(self) -> String { spellings.with(|us| us.borrow()[self.id].unique.clone()) }
    pub fn orig_sp(self) -> String { spellings.with(|us| us.borrow()[self.id].orig.clone()) }

    // Printable names are unique, like `unique_spelling`s, but are assigned during printing.
    // This way if the compiler freshens some name a bunch of times, producing a tomato-filled mess,
    // but only prints one version of the name, it gets to print an unadorned name.
    pub fn print(self) -> String {
        printables.with(|printables_| {
            printables_used.with(|printables_used_| {
                printables_
                    .borrow_mut()
                    .entry(self.id)
                    .or_insert_with(|| {
                        let mut print_version = self.orig_sp();
                        while printables_used_.borrow().contains(&print_version) {
                            // Graffiti seen at Berkley: "EⒶT YOUR VEGETABLES 🥕"
                            print_version = format!("{}🥕", print_version);
                        }
                        printables_used_.borrow_mut().insert(print_version.clone());
                        print_version.clone()
                    })
                    .clone()
            })
        })
    }

    pub fn global(s: &str) -> Name { Name::new(s, false) }
    pub fn gensym(s: &str) -> Name { Name::new(s, true) }
    pub fn freshen(self) -> Name { Name::new(&self.orig_sp(), true) }

    fn new(orig_spelling: &str, freshen: bool) -> Name {
        use std::borrow::{Borrow, BorrowMut};

        let fake_freshness_ = fake_freshness.with(|ff| *ff.borrow());

        id_map.with(|id_map_| {
            let mut unique_spelling = orig_spelling.to_owned();
            // Find a fresh version by adding tomatoes, if requested:
            while freshen && id_map_.borrow().contains_key(&unique_spelling) {
                unique_spelling = format!("{}🍅", unique_spelling);
            }

            if freshen && fake_freshness_ {
                // Forget doing it right; only add exactly one tomato:
                unique_spelling = format!("{}🍅", orig_spelling);
            }

            let claim_id = || {
                spellings.with(|spellings_| {
                    let new_id = spellings_.borrow().len();
                    spellings_.borrow_mut().push(Spelling {
                        unique: unique_spelling.clone(),
                        orig: orig_spelling.to_owned(),
                    });
                    new_id
                })
            };

            // If we're faking freshness, make the freshened name findable. Otherwise...
            let id = if freshen && !fake_freshness_ {
                claim_id() // ...don't put it in the table
            } else {
                *id_map_.borrow_mut().entry(unique_spelling.clone()).or_insert_with(claim_id)
            };

            Name { id: id }
        })
    }
    pub fn is(self, s: &str) -> bool { self.sp() == s }

    pub fn is_name(self, n: Name) -> bool { self.sp() == n.sp() }
}

// TODO: move to `ast_walk`
// TODO: using `lazy_static!` (with or without gensym) makes some tests fail. Why?
/// Special name for negative `ast_walk`ing
pub fn negative_ret_val() -> Name { Name::global("⋄") }

impl fmt::Debug for Name {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "«{}»", self.sp()) }
}

impl fmt::Display for Name {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.print()) }
}

pub fn n(s: &str) -> Name { Name::global(s) }

#[test]
fn name_interning() {
    // This test fails under tarpaulin; why? It must be related to `thread_local!` somehow...
    let a = n("a");
    assert_eq!(a, a);
    assert_eq!(a, n("a"));
    assert_ne!(a, a.freshen());

    assert_ne!(a, n("x🍅"));
    assert_ne!(a.freshen(), a.freshen());

    assert_ne!(n("a"), n("y"));

    enable_fake_freshness(true);

    let x = n("x");
    assert_eq!(x, x);
    assert_eq!(x, n("x"));
    assert_ne!(x, x.freshen());

    // ... but now we the freshened version of `x` is accessible (and doesn't avoid existing names)
    assert_eq!(x.freshen(), n("x🍅"));
    assert_eq!(x.freshen(), x.freshen());

    // Printable versions are first-come, first-served
    assert_eq!(a.freshen().print(), "a");
    assert_eq!(a.print(), "a🥕");
}
<commit_msg>Add `unhygenic_orig`, which we may need to deal with struct fields.<commit_after>#![macro_use]

use std::{
    cell::RefCell,
    collections::{HashMap, HashSet},
    fmt,
    string::String,
};

#[derive(PartialEq, Eq, Clone, Copy, Hash)]
pub struct Name {
    id: usize,
}

pub struct Spelling {
    // No two different variables have this the same. Tomatoes may have been added:
    unique: String,
    // The original spelling that the programmer chose.
    orig: String,
}

thread_local! {
    // From `Spelling.unique` to `id`s:
    static id_map: RefCell<HashMap<String, usize>> = RefCell::new(HashMap::new());
    // From `id`s to `Spelling`s
    static spellings: RefCell<Vec<Spelling>> = RefCell::new(vec![]);

    static printables: RefCell<HashMap<usize, String>> = RefCell::new(HashMap::new());
    // The values of `printables`, for lookup purposes.
    static printables_used: RefCell<HashSet<String>> = RefCell::new(HashSet::new());

    // Should we do "naive" freshening for testing purposes?
    static fake_freshness: RefCell<bool> = RefCell::new(false);
}

impl ::runtime::reify::Reifiable for Name {
    fn ty_name() -> Name { n("Name") }

    fn reify(&self) -> ::runtime::eval::Value {
        ::runtime::eval::Value::AbstractSyntax(::ast::Ast::Atom(*self))
    }

    fn reflect(v: &::runtime::eval::Value) -> Name {
        extract!((v) ::runtime::eval::Value::AbstractSyntax = (ref ast)
                          => ::core_forms::ast_to_name(ast))
    }
}

impl std::cmp::PartialOrd for Name {
    fn partial_cmp(&self, other: &Name) -> Option<std::cmp::Ordering> {
        Some(self.orig_sp().cmp(&other.orig_sp()))
    }
}

impl std::cmp::Ord for Name {
    fn cmp(&self, other: &Name) -> std::cmp::Ordering { self.orig_sp().cmp(&other.orig_sp()) }
}

// These are for isolating tests of alpha-equivalence from each other.

pub fn enable_fake_freshness(ff: bool) {
    use std::borrow::BorrowMut;

    fake_freshness.with(|fake_freshness_| {
        *fake_freshness_.borrow_mut() = ff;
    })
}

// only available on nightly:
// impl !Send for Name {}

impl Name {
    pub fn sp(self) -> String { spellings.with(|us| us.borrow()[self.id].unique.clone()) }
    pub fn orig_sp(self) -> String { spellings.with(|us| us.borrow()[self.id].orig.clone()) }

    /// This extracts the "original" `Name`, prior to any freshening.
    /// This is probably not ever the *right* thing to do, but may be needed as a workaround.
    pub fn unhygienic_orig(self) -> Name {
        spellings.with(|us| Name::new(&us.borrow()[self.id].orig, false))
    }

    // Printable names are unique, like `unique_spelling`s, but are assigned during printing.
    // This way if the compiler freshens some name a bunch of times, producing a tomato-filled mess,
    // but only prints one version of the name, it gets to print an unadorned name.
    pub fn print(self) -> String {
        printables.with(|printables_| {
            printables_used.with(|printables_used_| {
                printables_
                    .borrow_mut()
                    .entry(self.id)
                    .or_insert_with(|| {
                        let mut print_version = self.orig_sp();
                        while printables_used_.borrow().contains(&print_version) {
                            // Graffiti seen at Berkley: "EⒶT YOUR VEGETABLES 🥕"
                            print_version = format!("{}🥕", print_version);
                        }
                        printables_used_.borrow_mut().insert(print_version.clone());
                        print_version.clone()
                    })
                    .clone()
            })
        })
    }

    pub fn global(s: &str) -> Name { Name::new(s, false) }
    pub fn gensym(s: &str) -> Name { Name::new(s, true) }
    pub fn freshen(self) -> Name { Name::new(&self.orig_sp(), true) }

    fn new(orig_spelling: &str, freshen: bool) -> Name {
        use std::borrow::{Borrow, BorrowMut};

        let fake_freshness_ = fake_freshness.with(|ff| *ff.borrow());

        id_map.with(|id_map_| {
            let mut unique_spelling = orig_spelling.to_owned();
            // Find a fresh version by adding tomatoes, if requested:
            while freshen && id_map_.borrow().contains_key(&unique_spelling) {
                unique_spelling = format!("{}🍅", unique_spelling);
            }

            if freshen && fake_freshness_ {
                // Forget doing it right; only add exactly one tomato:
                unique_spelling = format!("{}🍅", orig_spelling);
            }

            let claim_id = || {
                spellings.with(|spellings_| {
                    let new_id = spellings_.borrow().len();
                    spellings_.borrow_mut().push(Spelling {
                        unique: unique_spelling.clone(),
                        orig: orig_spelling.to_owned(),
                    });
                    new_id
                })
            };

            // If we're faking freshness, make the freshened name findable. Otherwise...
            let id = if freshen && !fake_freshness_ {
                claim_id() // ...don't put it in the table
            } else {
                *id_map_.borrow_mut().entry(unique_spelling.clone()).or_insert_with(claim_id)
            };

            Name { id: id }
        })
    }
    pub fn is(self, s: &str) -> bool { self.sp() == s }

    pub fn is_name(self, n: Name) -> bool { self.sp() == n.sp() }
}

// TODO: move to `ast_walk`
// TODO: using `lazy_static!` (with or without gensym) makes some tests fail. Why?
/// Special name for negative `ast_walk`ing
pub fn negative_ret_val() -> Name { Name::global("⋄") }

impl fmt::Debug for Name {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "«{}»", self.sp()) }
}

impl fmt::Display for Name {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.print()) }
}

pub fn n(s: &str) -> Name { Name::global(s) }

#[test]
fn name_interning() {
    // This test fails under tarpaulin; why? It must be related to `thread_local!` somehow...
    let a = n("a");
    assert_eq!(a, a);
    assert_eq!(a, n("a"));
    assert_ne!(a, a.freshen());
    assert_eq!(a, a.freshen().unhygienic_orig());

    assert_ne!(a, n("x🍅"));
    assert_ne!(a.freshen(), a.freshen());

    assert_ne!(n("a"), n("y"));

    enable_fake_freshness(true);

    let x = n("x");
    assert_eq!(x, x);
    assert_eq!(x, n("x"));
    assert_ne!(x, x.freshen());

    // ... but now we the freshened version of `x` is accessible (and doesn't avoid existing names)
    assert_eq!(x.freshen(), n("x🍅"));
    assert_eq!(x.freshen(), x.freshen());

    // Printable versions are first-come, first-served
    assert_eq!(a.freshen().print(), "a");
    assert_eq!(a.print(), "a🥕");
}
<|endoftext|>


<commit_before>use crate::{
    api::{Error, LilaVariant},
    indexer::IndexerOpt,
    model::{GameId, Speed, UserName},
    util::ByColorDef,
};
use chrono::{DateTime, Utc};
use futures_util::stream::{Stream, StreamExt as _, TryStreamExt as _};
use serde::Deserialize;
use serde_with::{
    serde_as, DisplayFromStr, SpaceSeparator, StringWithSeparator, TimestampMilliSeconds,
};
use shakmaty::{fen::Fen, san::San, ByColor, Color};
use std::io;
use tokio::io::AsyncBufReadExt as _;
use tokio_stream::wrappers::LinesStream;
use tokio_util::io::StreamReader;

pub struct Lila {
    client: reqwest::Client,
    opt: IndexerOpt,
}

impl Lila {
    pub fn new(opt: IndexerOpt) -> Lila {
        Lila {
            client: reqwest::Client::builder().build().expect("reqwest client"),
            opt,
        }
    }

    pub async fn user_games(
        &self,
        user: &UserName,
        since_created_at: u64,
    ) -> Result<impl Stream<Item = Result<Game, Error>>, Error> {
        // https://lichess.org/api#operation/apiGamesUser
        let stream = self
            .client
            .get(format!(
                "{}/api/games/user/{}?sort=dateAsc&ongoing=true",
                self.opt.lila, user
            ))
            .query(&[("since", since_created_at.saturating_sub(1))])
            .header("Accept", "application/x-ndjson")
            .send()
            .await
            .and_then(|r| r.error_for_status())
            .map_err(Error::IndexerRequestError)?
            .bytes_stream()
            .map_err(|err| io::Error::new(io::ErrorKind::Other, err));

        Ok(Box::pin(
            LinesStream::new(StreamReader::new(stream).lines()).filter_map(|line| async move {
                match line {
                    Ok(line) if line.is_empty() => None,
                    Ok(line) => Some(serde_json::from_str::<Game>(&line).map_err(|err| {
                        Error::IndexerGameError {
                            err: err.into(),
                            line,
                        }
                    })),
                    Err(err) => Some(Err(Error::IndexerStreamError(err))),
                }
            }),
        ))
    }
}

#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Game {
    #[serde_as(as = "DisplayFromStr")]
    pub id: GameId,
    pub rated: bool,
    pub created_at: u64,
    #[serde_as(as = "TimestampMilliSeconds")]
    pub last_move_at: DateTime<Utc>,
    pub status: Status,
    pub variant: LilaVariant,
    #[serde(with = "ByColorDef")]
    pub players: ByColor<Player>,
    pub speed: Speed,
    #[serde_as(as = "StringWithSeparator::<SpaceSeparator, San>")]
    pub moves: Vec<San>,
    #[serde_as(as = "Option<DisplayFromStr>")]
    #[serde(default)]
    pub winner: Option<Color>,
    #[serde_as(as = "Option<DisplayFromStr>")]
    #[serde(default)]
    pub initial_fen: Option<Fen>,
}

impl Game {
    pub fn user_name(&self, color: Color) -> Option<&UserName> {
        self.players.by_color(color).user.as_ref().map(|u| &u.name)
    }
}

#[derive(Debug, Deserialize)]
pub struct Player {
    #[serde(default)]
    pub user: Option<User>,
    #[serde(default)]
    pub rating: Option<u16>,
}

#[serde_as]
#[derive(Debug, Deserialize)]
pub struct User {
    #[serde_as(as = "DisplayFromStr")]
    pub name: UserName,
}

#[derive(Debug, Deserialize, Copy, Clone)]
#[serde(rename_all = "camelCase")]
pub enum Status {
    Created,
    Started,
    Aborted,
    Mate,
    Resign,
    Stalemate,
    Timeout,
    Draw,
    #[serde(rename = "outoftime")]
    OutOfTime,
    Cheat,
    NoStart,
    UnknownFinish,
    VariantEnd,
}

impl Status {
    pub fn is_ongoing(self) -> bool {
        matches!(self, Status::Created | Status::Started)
    }

    pub fn is_unindexable(self) -> bool {
        matches!(
            self,
            Status::UnknownFinish | Status::NoStart | Status::Aborted
        )
    }
}
<commit_msg>remove date range hack (after ornicar/lila@7219cccda9f53021faee821e)<commit_after>use crate::{
    api::{Error, LilaVariant},
    indexer::IndexerOpt,
    model::{GameId, Speed, UserName},
    util::ByColorDef,
};
use chrono::{DateTime, Utc};
use futures_util::stream::{Stream, StreamExt as _, TryStreamExt as _};
use serde::Deserialize;
use serde_with::{
    serde_as, DisplayFromStr, SpaceSeparator, StringWithSeparator, TimestampMilliSeconds,
};
use shakmaty::{fen::Fen, san::San, ByColor, Color};
use std::io;
use tokio::io::AsyncBufReadExt as _;
use tokio_stream::wrappers::LinesStream;
use tokio_util::io::StreamReader;

pub struct Lila {
    client: reqwest::Client,
    opt: IndexerOpt,
}

impl Lila {
    pub fn new(opt: IndexerOpt) -> Lila {
        Lila {
            client: reqwest::Client::builder().build().expect("reqwest client"),
            opt,
        }
    }

    pub async fn user_games(
        &self,
        user: &UserName,
        since_created_at: u64,
    ) -> Result<impl Stream<Item = Result<Game, Error>>, Error> {
        // https://lichess.org/api#operation/apiGamesUser
        let stream = self
            .client
            .get(format!(
                "{}/api/games/user/{}?sort=dateAsc&ongoing=true",
                self.opt.lila, user
            ))
            .query(&[("since", since_created_at)])
            .header("Accept", "application/x-ndjson")
            .send()
            .await
            .and_then(|r| r.error_for_status())
            .map_err(Error::IndexerRequestError)?
            .bytes_stream()
            .map_err(|err| io::Error::new(io::ErrorKind::Other, err));

        Ok(Box::pin(
            LinesStream::new(StreamReader::new(stream).lines()).filter_map(|line| async move {
                match line {
                    Ok(line) if line.is_empty() => None,
                    Ok(line) => Some(serde_json::from_str::<Game>(&line).map_err(|err| {
                        Error::IndexerGameError {
                            err: err.into(),
                            line,
                        }
                    })),
                    Err(err) => Some(Err(Error::IndexerStreamError(err))),
                }
            }),
        ))
    }
}

#[serde_as]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Game {
    #[serde_as(as = "DisplayFromStr")]
    pub id: GameId,
    pub rated: bool,
    pub created_at: u64,
    #[serde_as(as = "TimestampMilliSeconds")]
    pub last_move_at: DateTime<Utc>,
    pub status: Status,
    pub variant: LilaVariant,
    #[serde(with = "ByColorDef")]
    pub players: ByColor<Player>,
    pub speed: Speed,
    #[serde_as(as = "StringWithSeparator::<SpaceSeparator, San>")]
    pub moves: Vec<San>,
    #[serde_as(as = "Option<DisplayFromStr>")]
    #[serde(default)]
    pub winner: Option<Color>,
    #[serde_as(as = "Option<DisplayFromStr>")]
    #[serde(default)]
    pub initial_fen: Option<Fen>,
}

impl Game {
    pub fn user_name(&self, color: Color) -> Option<&UserName> {
        self.players.by_color(color).user.as_ref().map(|u| &u.name)
    }
}

#[derive(Debug, Deserialize)]
pub struct Player {
    #[serde(default)]
    pub user: Option<User>,
    #[serde(default)]
    pub rating: Option<u16>,
}

#[serde_as]
#[derive(Debug, Deserialize)]
pub struct User {
    #[serde_as(as = "DisplayFromStr")]
    pub name: UserName,
}

#[derive(Debug, Deserialize, Copy, Clone)]
#[serde(rename_all = "camelCase")]
pub enum Status {
    Created,
    Started,
    Aborted,
    Mate,
    Resign,
    Stalemate,
    Timeout,
    Draw,
    #[serde(rename = "outoftime")]
    OutOfTime,
    Cheat,
    NoStart,
    UnknownFinish,
    VariantEnd,
}

impl Status {
    pub fn is_ongoing(self) -> bool {
        matches!(self, Status::Created | Status::Started)
    }

    pub fn is_unindexable(self) -> bool {
        matches!(
            self,
            Status::UnknownFinish | Status::NoStart | Status::Aborted
        )
    }
}
<|endoftext|>


<commit_before>use nom::{IResult, alpha, alphanumeric, digit, double, space};

use std::str;
use std::str::FromStr;
use std::collections::HashMap;

use node;
use node::NodeKind;

use id::IdGen;

use typing::Type;

use std::boxed::Box;

// syntax reference: https://caml.inria.fr/pub/docs/manual-ocaml/language.html

fn to_str(slice: &[u8]) -> &str {
    str::from_utf8(slice).unwrap()
}

named!(comment<()>, do_parse!(
    tag!("(*") >>
    take_until!("*)") >> ()
));

named!(whitespace<()>, do_parse!(
    one_of!(" \t\n\r") >> ()
));

named!(opt_spaces<()>, do_parse!(
    many0!(alt!(whitespace | comment)) >> ()
));

named!(spaces<()>, do_parse!(
    many1!(alt!(whitespace | comment)) >> ()
));

named!(funcdef<NodeKind>,
    do_parse!(
        name:   ident >> // TODO: not only identifier... (https://caml.inria.fr/pub/docs/manual-ocaml/patterns.html#pattern)
        params: many1!(do_parse!(spaces >> param: ident >> (param))) >>
        (NodeKind::FuncDef(
                (name.get_ident_name().unwrap(), Type::Var(0)),
                (params.into_iter().map(|param| ( param.get_ident_name().unwrap(), Type::Var(0) ) ).collect())
                )
        )
    )
);

named!(expr_let<NodeKind>,
    do_parse!(
        tag!("let") >>
        spaces >>
        name: alt!(funcdef | ident) >>
        ws!(tag!("=")) >>
        exp: expr >>
        spaces >>
        tag!("in") >>
        spaces >>
        body: expr >>
        (match name {
            NodeKind::FuncDef(name, params) => NodeKind::LetFuncExpr(
                                                node::FuncDef { name: name, params: params },
                                                Box::new(exp),
                                                Box::new(body)
                                               ),
            NodeKind::Ident(name)           => NodeKind::LetExpr(
                                                (name, Type::Var(0)),
                                                Box::new(exp),
                                                Box::new(body)
                                               ),
            _                               => panic!()
        })
    )
);

named!(expr<NodeKind>,
    alt!(
            expr_let
        |   expr_add_sub
    )
);

named!(expr_mul_div<NodeKind>,
    do_parse!(
        init: expr_unary >>
        res:  fold_many0!(
                do_parse!(
                    opt_spaces >>
                    op: alt!(tag!("*.") | tag!("/.") | tag!("*") | tag!("/")) >>
                    opt_spaces >>
                    rhs: expr_mul_div >>
                    (op, rhs)
                ),
                init,
                |n1, (op, n2): (&[u8], NodeKind)| {
                    let (op, is_int) = node::str_to_binop(str::from_utf8(op).unwrap());
                    if is_int { NodeKind::IntBinaryOp(op, Box::new(n1), Box::new(n2)) }
                    else { NodeKind::FloatBinaryOp(op, Box::new(n1), Box::new(n2)) }
                }
        ) >> (res)
    )
);

named!(expr_add_sub<NodeKind>,
    do_parse!(
        init: expr_mul_div >>
        res:  fold_many0!(
                do_parse!(
                    opt_spaces >>
                    op: alt!(tag!("+.") | tag!("-.") | tag!("+") | tag!("-")) >>
                    opt_spaces >>
                    rhs: expr_mul_div >>
                    (op, rhs)
                ),
                init,
                |n1, (op, n2): (&[u8], NodeKind)| {
                    let (op, is_int) = node::str_to_binop(str::from_utf8(op).unwrap());
                    if is_int { NodeKind::IntBinaryOp(op, Box::new(n1), Box::new(n2)) }
                    else { NodeKind::FloatBinaryOp(op, Box::new(n1), Box::new(n2)) }
                }
        ) >> (res)
    )
);

named!(expr_func_call<NodeKind>,
    do_parse!(
        p: expr_prim >>
        args:   fold_many0!(
                expr,
                Vec::new(),
                |mut a: Vec<NodeKind>, arg: NodeKind| {
                    a.push(arg);
                    a
                }
        ) >> (if args.len() == 0 { p } else { NodeKind::Call(Box::new(p), args) })
    )
);

named!(expr_unary<NodeKind>,
    alt!(
        do_parse!(
            opt_spaces >>
            op: alt!(tag!("-.") | tag!("-")) >>
            opt_spaces >>
            e: expr_unary >>
            (NodeKind::UnaryOp(node::str_to_unaryop(str::from_utf8(op).unwrap()), Box::new(e)))
        ) |
        expr_postfix
    )
);

named!(apply_postfix<Vec<NodeKind>>, do_parse!(
    spaces >>
    args: separated_nonempty_list_complete!(spaces, expr_prim) >>
    (args)
));

named!(expr_postfix<NodeKind>,
    do_parse!(
        init: expr_prim >>
        folded: fold_many0!(
            apply_postfix,
            init,
            |lhs, pf| {
                NodeKind::Call(Box::new(lhs), pf)
            }
        ) >> (folded)
    )
);

named!(expr_prim<NodeKind>,
    alt!(
          constant
        | parens
    )
);

named!(integer<NodeKind>,
    do_parse!(
        i: map_res!(map_res!(
            digit,
            str::from_utf8
        ), FromStr::from_str) >>
        (NodeKind::Int(i))
    )
);

named!(float<NodeKind>,
    do_parse!(
        f: double >>
        (NodeKind::Float(f))
    )
);

fn is_ident(x: &[u8]) -> bool {
    let keywords = vec![&b"let"[..], &b"rec"[..], &b"in"[..], &b"true"[..],
                        &b"false"[..], &b"if"[..], &b"then"[..], &b"else"[..],
                        &b"Array.create"[..], &b"Array.make"[..]];
    if x.len() == 0 || keywords.contains(&x) {
        return false;
    }
    !(b'0' <= x[0] && x[0] <= b'9')
}
fn is_not_ident_u8(x: u8) -> bool {
    !((b'0' <= x && x <= b'9') || (b'A' <= x && x <= b'Z') || (b'a' <= x && x <= b'z') || x == b'_')
}

named!(ident<NodeKind>, do_parse!(
    i: verify!(take_till!(is_not_ident_u8), is_ident) >>
    (NodeKind::Ident(String::from_utf8(i.to_vec()).unwrap()))
));

named!(bool_true<NodeKind>,
    do_parse!( tag!("true") >> (NodeKind::Bool(true)) )
);

named!(bool_false<NodeKind>,
    do_parse!( tag!("false") >> (NodeKind::Bool(false)) )
);

named!(constant<NodeKind>,
    alt_complete!(float | integer | ident | bool_false | bool_true)
);

named!(parens<NodeKind>, delimited!(tag!("("), expr, tag!(")")));

named!(opt_dscolon<()>, do_parse!(
    many0!(tag!(";;")) >> ()
));

named!(module_item<NodeKind>,
    do_parse!(
        ws!(opt_dscolon) >>
        i: alt!(expr | definition) >>
        opt_spaces >>
        opt_dscolon >> (i)
    )
);

named!(definition<NodeKind>,
    alt!(
        definition_let
    )
);

named!(definition_let<NodeKind>,
    do_parse!(
        tag!("let") >>
        spaces >>
        name: alt!(funcdef | ident) >>
        ws!(tag!("=")) >>
        exp: expr >>
        (match name {
            NodeKind::FuncDef(name, params) => NodeKind::LetFuncDef(
                                                node::FuncDef { name: name, params: params },
                                                Box::new(exp)),
            NodeKind::Ident(name)           => NodeKind::LetDef(
                                                (name, Type::Var(0)),
                                                Box::new(exp)
                                               ),
            _                               => panic!()
        })
    )
);

pub fn uniquify(expr: NodeKind, idgen: &mut IdGen) -> NodeKind {
    match expr {
        NodeKind::LetExpr((name, ty), expr, body) => {
            let ty = if let Type::Var(_) = ty {
                idgen.get_type()
            } else {
                ty
            };
            let expr = uniquify(*expr, idgen);
            let body = uniquify(*body, idgen);
            NodeKind::LetExpr((name, ty), Box::new(expr), Box::new(body))
        }
        NodeKind::LetFuncExpr(node::FuncDef {
                                  name: (name, t),
                                  mut params,
                              },
                              expr,
                              body) => {
            let t = if let Type::Var(_) = t {
                idgen.get_type()
            } else {
                t
            };
            for i in 0..params.len() {
                let entry = ::std::mem::replace(&mut params[i].1, Type::Unit);
                let new_ty = if let Type::Var(_) = entry {
                    idgen.get_type()
                } else {
                    entry
                };
                params[i].1 = new_ty;
            }
            let expr = Box::new(uniquify(*expr, idgen));
            let body = Box::new(uniquify(*body, idgen));
            NodeKind::LetFuncExpr(
                node::FuncDef {
                    name: (name, t),
                    params: params,
                },
                expr,
                body,
            )
        }
        NodeKind::IntBinaryOp(op, e1, e2) => {
            let e1 = Box::new(uniquify(*e1, idgen));
            let e2 = Box::new(uniquify(*e2, idgen));
            NodeKind::IntBinaryOp(op, e1, e2)
        }
        NodeKind::FloatBinaryOp(op, e1, e2) => {
            let e1 = Box::new(uniquify(*e1, idgen));
            let e2 = Box::new(uniquify(*e2, idgen));
            NodeKind::FloatBinaryOp(op, e1, e2)
        }
        NodeKind::Call(e1, mut e2s) => {
            let e1 = Box::new(uniquify(*e1, idgen));
            uniquify_seq(&mut e2s, idgen);
            NodeKind::Call(e1, e2s)
        }
        x => x, // No Syntax inside
    }
}

fn uniquify_seq(seq: &mut Vec<NodeKind>, id_gen: &mut IdGen) {
    for i in 0..seq.len() {
        let entry = ::std::mem::replace(&mut seq[i], NodeKind::Unit);
        seq[i] = uniquify(entry, id_gen);
    }
}

pub fn parse_simple_expr(e: &str) {
    println!("expr: {}\n{}", e, match expr(e.as_bytes()) {
        IResult::Done(_, expr_node) => format!("generated node: {:?}", expr_node),
        IResult::Incomplete(needed) => format!("imcomplete: {:?}",     needed),
        IResult::Error(err) =>         format!("error: {:?}",          err)
    });
}

pub fn parse_module_item_expr(e: &str) {
    println!("module-item: {}\n{}", e, match module_item(e.as_bytes()) {
        IResult::Done(_, expr_node) => format!("generated node: {:?}", expr_node),
        IResult::Incomplete(needed) => format!("imcomplete: {:?}",     needed),
        IResult::Error(err) =>         format!("error: {:?}",          err)
    });
}

pub fn parse_and_infer_type(e: &str) {
    println!("expr: {}", e);
    let node = match module_item(e.as_bytes()) {
        IResult::Done(_, node) => node,
        _ => panic!(),
    };
    // sloppy impl of showing type-infered node
    use typing;
    use id;
    let mut idgen = id::IdGen::new();
    let uniquified = uniquify(node, &mut idgen);
    println!("generated node: {:?}\ntype infered node: {:?}", uniquified, typing::f(&uniquified, &mut idgen));
}

#[test]
pub fn test_parse_simple_expr() {
    use node::NodeKind::*;
    use node::BinOps::*;

    let f = |e: &str| match expr(e.as_bytes()) {
        IResult::Done(_, expr_node) => expr_node,
        IResult::Incomplete(needed) => panic!(format!("imcomplete: {:?}",     needed)),
        IResult::Error(err) => panic!(format!("error: {:?}", err)),
    };

    assert_eq!(f("5 / a3 + 11 * 10"),
               IntBinaryOp(IAdd,
                        Box::new(IntBinaryOp(IDiv,
                                          Box::new(Int(5)),
                                          Box::new(Ident("a3".to_string())))),
                        Box::new(IntBinaryOp(IMul,
                                          Box::new(Int(11)),
                                          Box::new(Int(10))))));
    assert_eq!(f("5.3 *. 10.2"),
               FloatBinaryOp(FMul,
                        Box::new(Float(5.3)),
                        Box::new(Float(10.2))))
}

#[test]
pub fn test_parse_module_item() {
    use node::NodeKind::*;
    use node::FuncDef;
    use node::BinOps::*;
    use node::BinOps::*;

    let f = |e: &str| match module_item(e.as_bytes()) {
        IResult::Done(_, expr_node) => expr_node,
        IResult::Incomplete(needed) => panic!(format!("imcomplete: {:?}",     needed)),
        IResult::Error(err) => panic!(format!("error: {:?}", err)),
    };

    assert_eq!(f("let f x = x * 2;;"),
                LetFuncDef(
                    FuncDef { name: ("f".to_string(), Type::Var(0)),
                              params: vec![("x".to_string(), Type::Var(0))] },
                              Box::new(IntBinaryOp(IMul,
                                                   Box::new(Ident("x".to_string())),
                                                   Box::new(Int(2))))));
    // assert_eq!(f("5.3 *. 10.2"),
    //            FloatBinaryOp(FMul,
    //                     Box::new(Float(5.3)),
    //                     Box::new(Float(10.2))))
}
<commit_msg>refine code<commit_after>use nom::{IResult, alpha, alphanumeric, digit, double, space};

use std::str;
use std::str::FromStr;
use std::collections::HashMap;

use node;
use node::NodeKind;

use id::IdGen;

use typing::Type;

use std::boxed::Box;

// syntax reference: https://caml.inria.fr/pub/docs/manual-ocaml/language.html

fn to_str(slice: &[u8]) -> &str {
    str::from_utf8(slice).unwrap()
}

named!(comment<()>, do_parse!(
    tag!("(*") >>
    take_until!("*)") >> ()
));

named!(whitespace<()>, do_parse!(
    one_of!(" \t\n\r") >> ()
));

named!(opt_spaces<()>, do_parse!(
    many0!(alt!(whitespace | comment)) >> ()
));

named!(spaces<()>, do_parse!(
    many1!(alt!(whitespace | comment)) >> ()
));

named!(funcdef<NodeKind>,
    do_parse!(
        name:   ident >> // TODO: not only identifier... (https://caml.inria.fr/pub/docs/manual-ocaml/patterns.html#pattern)
        params: many1!(do_parse!(spaces >> param: ident >> (param))) >>
        (NodeKind::FuncDef(
                (name.get_ident_name().unwrap(), Type::Var(0)),
                (params.into_iter().map(|param| ( param.get_ident_name().unwrap(), Type::Var(0) ) ).collect())
                )
        )
    )
);

named!(expr_let<NodeKind>,
    do_parse!(
        tag!("let") >>
        spaces >>
        name: alt!(funcdef | ident) >>
        ws!(tag!("=")) >>
        exp: expr >>
        spaces >>
        tag!("in") >>
        spaces >>
        body: expr >>
        (match name {
            NodeKind::FuncDef(name, params) => NodeKind::LetFuncExpr(
                                                node::FuncDef { name: name, params: params },
                                                Box::new(exp),
                                                Box::new(body)
                                               ),
            NodeKind::Ident(name)           => NodeKind::LetExpr(
                                                (name, Type::Var(0)),
                                                Box::new(exp),
                                                Box::new(body)
                                               ),
            _                               => panic!()
        })
    )
);

named!(expr<NodeKind>,
    alt!(
            expr_let
        |   expr_add_sub
    )
);

named!(expr_mul_div<NodeKind>,
    do_parse!(
        init: expr_unary >>
        res:  fold_many0!(
                do_parse!(
                    opt_spaces >>
                    op: alt!(tag!("*.") | tag!("/.") | tag!("*") | tag!("/")) >>
                    opt_spaces >>
                    rhs: expr_mul_div >>
                    (op, rhs)
                ),
                init,
                |n1, (op, n2): (&[u8], NodeKind)| {
                    let (op, is_int) = node::str_to_binop(str::from_utf8(op).unwrap());
                    if is_int { NodeKind::IntBinaryOp(op, Box::new(n1), Box::new(n2)) }
                    else { NodeKind::FloatBinaryOp(op, Box::new(n1), Box::new(n2)) }
                }
        ) >> (res)
    )
);

named!(expr_add_sub<NodeKind>,
    do_parse!(
        init: expr_mul_div >>
        res:  fold_many0!(
                do_parse!(
                    opt_spaces >>
                    op: alt!(tag!("+.") | tag!("-.") | tag!("+") | tag!("-")) >>
                    opt_spaces >>
                    rhs: expr_mul_div >>
                    (op, rhs)
                ),
                init,
                |n1, (op, n2): (&[u8], NodeKind)| {
                    let (op, is_int) = node::str_to_binop(str::from_utf8(op).unwrap());
                    if is_int { NodeKind::IntBinaryOp(op, Box::new(n1), Box::new(n2)) }
                    else { NodeKind::FloatBinaryOp(op, Box::new(n1), Box::new(n2)) }
                }
        ) >> (res)
    )
);

named!(expr_func_call<NodeKind>,
    do_parse!(
        p: expr_prim >>
        args:   fold_many0!(
                expr,
                Vec::new(),
                |mut a: Vec<NodeKind>, arg: NodeKind| {
                    a.push(arg);
                    a
                }
        ) >> (if args.len() == 0 { p } else { NodeKind::Call(Box::new(p), args) })
    )
);

named!(expr_unary<NodeKind>,
    alt!(
        do_parse!(
            opt_spaces >>
            op: alt!(tag!("-.") | tag!("-")) >>
            opt_spaces >>
            e: expr_unary >>
            (NodeKind::UnaryOp(node::str_to_unaryop(str::from_utf8(op).unwrap()), Box::new(e)))
        ) |
        expr_postfix
    )
);

named!(apply_postfix<Vec<NodeKind>>, do_parse!(
    spaces >>
    args: separated_nonempty_list_complete!(spaces, expr_prim) >>
    (args)
));

named!(expr_postfix<NodeKind>,
    do_parse!(
        init: expr_prim >>
        folded: fold_many0!(
            apply_postfix,
            init,
            |lhs, pf| {
                NodeKind::Call(Box::new(lhs), pf)
            }
        ) >> (folded)
    )
);

named!(expr_prim<NodeKind>,
    alt!(
          constant
        | parens
    )
);

named!(integer<NodeKind>,
    do_parse!(
        i: map_res!(map_res!(
            digit,
            str::from_utf8
        ), FromStr::from_str) >>
        (NodeKind::Int(i))
    )
);

named!(float<NodeKind>,
    do_parse!(
        f: double >>
        (NodeKind::Float(f))
    )
);

fn is_ident(x: &[u8]) -> bool {
    let keywords = vec![&b"let"[..], &b"rec"[..], &b"in"[..], &b"true"[..],
                        &b"false"[..], &b"if"[..], &b"then"[..], &b"else"[..],
                        &b"Array.create"[..], &b"Array.make"[..]];
    if x.len() == 0 || keywords.contains(&x) {
        return false;
    }
    !(b'0' <= x[0] && x[0] <= b'9')
}
fn is_not_ident_u8(x: u8) -> bool {
    !((b'0' <= x && x <= b'9') || (b'A' <= x && x <= b'Z') || (b'a' <= x && x <= b'z') || x == b'_')
}

named!(ident<NodeKind>, do_parse!(
    i: verify!(take_till!(is_not_ident_u8), is_ident) >>
    (NodeKind::Ident(String::from_utf8(i.to_vec()).unwrap()))
));

named!(bool_true<NodeKind>,
    do_parse!( tag!("true") >> (NodeKind::Bool(true)) )
);

named!(bool_false<NodeKind>,
    do_parse!( tag!("false") >> (NodeKind::Bool(false)) )
);

named!(constant<NodeKind>,
    alt_complete!(float | integer | ident | bool_false | bool_true)
);

named!(parens<NodeKind>, delimited!(tag!("("), expr, tag!(")")));

named!(opt_dscolon<()>, do_parse!(
    many0!(tag!(";;")) >> ()
));

named!(module_item<NodeKind>,
    do_parse!(
        ws!(opt_dscolon) >>
        i: alt!(expr | definition) >>
        opt_spaces >>
        opt_dscolon >> (i)
    )
);

named!(definition<NodeKind>,
    alt!(
        definition_let
    )
);

named!(definition_let<NodeKind>,
    do_parse!(
        tag!("let") >>
        spaces >>
        name: alt!(funcdef | ident) >>
        ws!(tag!("=")) >>
        exp: expr >>
        (match name {
            NodeKind::FuncDef(name, params) => NodeKind::LetFuncDef(
                                                node::FuncDef { name: name, params: params },
                                                Box::new(exp)),
            NodeKind::Ident(name)           => NodeKind::LetDef(
                                                (name, Type::Var(0)),
                                                Box::new(exp)
                                               ),
            _                               => panic!()
        })
    )
);

pub fn uniquify(expr: NodeKind, idgen: &mut IdGen) -> NodeKind {
    match expr {
        NodeKind::LetExpr((name, ty), expr, body) => {
            let ty = if let Type::Var(_) = ty {
                idgen.get_type()
            } else {
                ty
            };
            let expr = uniquify(*expr, idgen);
            let body = uniquify(*body, idgen);
            NodeKind::LetExpr((name, ty), Box::new(expr), Box::new(body))
        }
        NodeKind::LetDef((name, ty), expr) => {
            let ty = if let Type::Var(_) = ty {
                idgen.get_type()
            } else {
                ty
            };
            let expr = uniquify(*expr, idgen);
            NodeKind::LetDef((name, ty), Box::new(expr))
        }
        NodeKind::LetFuncExpr(node::FuncDef {
                                  name: (name, t),
                                  mut params,
                              },
                              expr,
                              body) => {
            let t = if let Type::Var(_) = t {
                idgen.get_type()
            } else {
                t
            };
            for &mut (ref mut param_name, ref mut param_ty) in &mut params {
                let entry = ::std::mem::replace(param_ty, Type::Unit);
                let new_ty = if let Type::Var(_) = entry {
                    idgen.get_type()
                } else {
                    entry
                };
                *param_ty = new_ty;
            }
            let expr = Box::new(uniquify(*expr, idgen));
            let body = Box::new(uniquify(*body, idgen));
            NodeKind::LetFuncExpr(
                node::FuncDef {
                    name: (name, t),
                    params: params,
                },
                expr,
                body,
            )
        }
        NodeKind::LetFuncDef(node::FuncDef {
                                 name: (name, t),
                                 mut params,
                             },
                             expr) => {
            let t = if let Type::Var(_) = t {
                idgen.get_type()
            } else {
                t
            };
            for &mut (ref mut param_name, ref mut param_ty) in &mut params {
                let entry = ::std::mem::replace(param_ty, Type::Unit);
                let new_ty = if let Type::Var(_) = entry {
                    idgen.get_type()
                } else {
                    entry
                };
                *param_ty = new_ty;
            }
            let expr = Box::new(uniquify(*expr, idgen));
            NodeKind::LetFuncDef(
                node::FuncDef {
                    name: (name, t),
                    params: params,
                },
                expr,
            )
        }
        NodeKind::IntBinaryOp(op, e1, e2) => {
            let e1 = Box::new(uniquify(*e1, idgen));
            let e2 = Box::new(uniquify(*e2, idgen));
            NodeKind::IntBinaryOp(op, e1, e2)
        }
        NodeKind::FloatBinaryOp(op, e1, e2) => {
            let e1 = Box::new(uniquify(*e1, idgen));
            let e2 = Box::new(uniquify(*e2, idgen));
            NodeKind::FloatBinaryOp(op, e1, e2)
        }
        NodeKind::Call(e1, mut e2s) => {
            let e1 = Box::new(uniquify(*e1, idgen));
            uniquify_seq(&mut e2s, idgen);
            NodeKind::Call(e1, e2s)
        }
        x => x, // No Syntax inside
    }
}

fn uniquify_seq(seq: &mut Vec<NodeKind>, id_gen: &mut IdGen) {
    for i in 0..seq.len() {
        let entry = ::std::mem::replace(&mut seq[i], NodeKind::Unit);
        seq[i] = uniquify(entry, id_gen);
    }
}

pub fn parse_simple_expr(e: &str) {
    println!("expr: {}\n{}", e, match expr(e.as_bytes()) {
        IResult::Done(_, expr_node) => format!("generated node: {:?}", expr_node),
        IResult::Incomplete(needed) => format!("imcomplete: {:?}",     needed),
        IResult::Error(err) =>         format!("error: {:?}",          err)
    });
}

pub fn parse_module_item_expr(e: &str) {
    println!("module-item: {}\n{}", e, match module_item(e.as_bytes()) {
        IResult::Done(_, expr_node) => format!("generated node: {:?}", expr_node),
        IResult::Incomplete(needed) => format!("imcomplete: {:?}",     needed),
        IResult::Error(err) =>         format!("error: {:?}",          err)
    });
}

pub fn parse_and_infer_type(e: &str) {
    println!("expr: {}", e);
    let node = match module_item(e.as_bytes()) {
        IResult::Done(_, node) => node,
        _ => panic!(),
    };
    // sloppy impl of showing type-infered node
    use typing;
    use id;
    let mut idgen = id::IdGen::new();
    let uniquified = uniquify(node, &mut idgen);
    println!("generated node: {:?}\ntype infered node: {:?}", uniquified, typing::f(&uniquified, &mut idgen));
}

#[test]
pub fn test_parse_simple_expr() {
    use node::NodeKind::*;
    use node::BinOps::*;

    let f = |e: &str| match expr(e.as_bytes()) {
        IResult::Done(_, expr_node) => expr_node,
        IResult::Incomplete(needed) => panic!(format!("imcomplete: {:?}",     needed)),
        IResult::Error(err) => panic!(format!("error: {:?}", err)),
    };

    assert_eq!(f("5 / a3 + 11 * 10"),
               IntBinaryOp(IAdd,
                        Box::new(IntBinaryOp(IDiv,
                                          Box::new(Int(5)),
                                          Box::new(Ident("a3".to_string())))),
                        Box::new(IntBinaryOp(IMul,
                                          Box::new(Int(11)),
                                          Box::new(Int(10))))));
    assert_eq!(f("5.3 *. 10.2"),
               FloatBinaryOp(FMul,
                        Box::new(Float(5.3)),
                        Box::new(Float(10.2))))
}

#[test]
pub fn test_parse_module_item() {
    use node::NodeKind::*;
    use node::FuncDef;
    use node::BinOps::*;
    use node::BinOps::*;

    let f = |e: &str| match module_item(e.as_bytes()) {
        IResult::Done(_, expr_node) => expr_node,
        IResult::Incomplete(needed) => panic!(format!("imcomplete: {:?}",     needed)),
        IResult::Error(err) => panic!(format!("error: {:?}", err)),
    };

    assert_eq!(f("let f x = x * 2;;"),
                LetFuncDef(
                    FuncDef { name: ("f".to_string(), Type::Var(0)),
                              params: vec![("x".to_string(), Type::Var(0))] },
                              Box::new(IntBinaryOp(IMul,
                                                   Box::new(Ident("x".to_string())),
                                                   Box::new(Int(2))))));
    // assert_eq!(f("5.3 *. 10.2"),
    //            FloatBinaryOp(FMul,
    //                     Box::new(Float(5.3)),
    //                     Box::new(Float(10.2))))
}
<|endoftext|>


<commit_before>use nom::{IResult, not_line_ending, line_ending};

// http://rust.unhandledexpression.com/nom/macro.escaped!.html
// http://rust.unhandledexpression.com/nom/
// https://fr.wikipedia.org/wiki/Comma-separated_values
// https://github.com/Geal/nom/wiki/Making-a-new-parser-from-scratch
// https://github.com/Geal/nom
fn csv_line(input: &[u8]) -> IResult<&[u8], Vec<&[u8]>> {
    terminated!(input, separated_list!(is_not_bytes!(&b"\n\r,"[..]), not_line_ending), line_ending)
}

fn parse_string_between_quotes(input: &str) {

}

fn get_data(input: &str) -> Vec<Vec<String>> {

}

#[test]
fn check_file() {
    let f = b"nom,age\ncarles,30\nlaure,28";

    csv_line(f);
}
<commit_msg>First working macro<commit_after>use nom::*;

// http://rust.unhandledexpression.com/nom/macro.escaped!.html
// http://rust.unhandledexpression.com/nom/
// https://fr.wikipedia.org/wiki/Comma-separated_values
// https://github.com/Geal/nom/wiki/Making-a-new-parser-from-scratch
// https://github.com/Geal/nom
// http://rust.unhandledexpression.com/nom/macro.escaped!.html
named!(string_between_quotes, delimited!(char!('\"'), is_not!("\""), char!('\"')));

#[test]
fn check_file() {
    let f = b"\"nom\",age\ncarles,30\nlaure,28";

    match string_between_quotes(f) {
        IResult::Done(_, out) => assert_eq!(out, b"nom"),
        IResult::Incomplete(x) => panic!("incomplete: {:?}", x),
        IResult::Error(e) => panic!("error: {:?}", e),
    }
}
<|endoftext|>


<commit_before>use std::{env, slice, str::FromStr};

use super::*;
use crate::{dpi::validate_hidpi_factor, monitor::VideoMode};

pub fn calc_dpi_factor(
    (width_px, height_px): (u32, u32),
    (width_mm, height_mm): (u64, u64),
) -> f64 {
    // Override DPI if `WINIT_HIDPI_FACTOR` variable is set
    let dpi_override = env::var("WINIT_HIDPI_FACTOR")
        .ok()
        .and_then(|var| f64::from_str(&var).ok());
    if let Some(dpi_override) = dpi_override {
        if !validate_hidpi_factor(dpi_override) {
            panic!(
                "`WINIT_HIDPI_FACTOR` invalid; DPI factors must be normal floats greater than 0. Got `{}`",
                dpi_override,
            );
        }
        return dpi_override;
    }

    // See http://xpra.org/trac/ticket/728 for more information.
    if width_mm == 0 || width_mm == 0 {
        warn!("XRandR reported that the display's 0mm in size, which is certifiably insane");
        return 1.0;
    }

    let ppmm = ((width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)).sqrt();
    // Quantize 1/12 step size
    let dpi_factor = ((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0);
    assert!(validate_hidpi_factor(dpi_factor));
    dpi_factor
}

pub enum MonitorRepr {
    Monitor(*mut ffi::XRRMonitorInfo),
    Crtc(*mut ffi::XRRCrtcInfo),
}

impl MonitorRepr {
    pub unsafe fn get_output(&self) -> ffi::RROutput {
        match *self {
            // Same member names, but different locations within the struct...
            MonitorRepr::Monitor(monitor) => *((*monitor).outputs.offset(0)),
            MonitorRepr::Crtc(crtc) => *((*crtc).outputs.offset(0)),
        }
    }

    pub unsafe fn size(&self) -> (u32, u32) {
        match *self {
            MonitorRepr::Monitor(monitor) => ((*monitor).width as u32, (*monitor).height as u32),
            MonitorRepr::Crtc(crtc) => ((*crtc).width as u32, (*crtc).height as u32),
        }
    }

    pub unsafe fn position(&self) -> (i32, i32) {
        match *self {
            MonitorRepr::Monitor(monitor) => ((*monitor).x as i32, (*monitor).y as i32),
            MonitorRepr::Crtc(crtc) => ((*crtc).x as i32, (*crtc).y as i32),
        }
    }
}

impl From<*mut ffi::XRRMonitorInfo> for MonitorRepr {
    fn from(monitor: *mut ffi::XRRMonitorInfo) -> Self {
        MonitorRepr::Monitor(monitor)
    }
}

impl From<*mut ffi::XRRCrtcInfo> for MonitorRepr {
    fn from(crtc: *mut ffi::XRRCrtcInfo) -> Self {
        MonitorRepr::Crtc(crtc)
    }
}

impl XConnection {
    // Retrieve DPI from Xft.dpi property
    pub unsafe fn get_xft_dpi(&self) -> Option<f64> {
        (self.xlib.XrmInitialize)();
        let resource_manager_str = (self.xlib.XResourceManagerString)(self.display);
        if resource_manager_str == ptr::null_mut() {
            return None;
        }
        if let Ok(res) = ::std::ffi::CStr::from_ptr(resource_manager_str).to_str() {
            let name: &str = "Xft.dpi:\t";
            for pair in res.split("\n") {
                if pair.starts_with(&name) {
                    let res = &pair[name.len()..];
                    return f64::from_str(&res).ok();
                }
            }
        }
        None
    }
    pub unsafe fn get_output_info(
        &self,
        resources: *mut ffi::XRRScreenResources,
        repr: &MonitorRepr,
    ) -> Option<(String, f64, Vec<VideoMode>)> {
        let output_info =
            (self.xrandr.XRRGetOutputInfo)(self.display, resources, repr.get_output());
        if output_info.is_null() {
            // When calling `XRRGetOutputInfo` on a virtual monitor (versus a physical display)
            // it's possible for it to return null.
            // https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=816596
            let _ = self.check_errors(); // discard `BadRROutput` error
            return None;
        }

        let screen = (self.xlib.XDefaultScreen)(self.display);
        let bit_depth = (self.xlib.XDefaultDepth)(self.display, screen);

        let output_modes =
            slice::from_raw_parts((*output_info).modes, (*output_info).nmode as usize);
        let resource_modes = slice::from_raw_parts((*resources).modes, (*resources).nmode as usize);

        let modes = resource_modes
            .iter()
            // XRROutputInfo contains an array of mode ids that correspond to
            // modes in the array in XRRScreenResources
            .filter(|x| output_modes.iter().any(|id| x.id == *id))
            .map(|x| {
                let refresh_rate = if x.dotClock > 0 && x.hTotal > 0 && x.vTotal > 0 {
                    x.dotClock as u64 * 1000 / (x.hTotal as u64 * x.vTotal as u64)
                } else {
                    0
                };

                VideoMode {
                    size: (x.width, x.height),
                    refresh_rate: (refresh_rate as f32 / 1000.0).round() as u16,
                    bit_depth: bit_depth as u16,
                }
            });

        let name_slice = slice::from_raw_parts(
            (*output_info).name as *mut u8,
            (*output_info).nameLen as usize,
        );
        let name = String::from_utf8_lossy(name_slice).into();
        let hidpi_factor = if let Some(dpi) = self.get_xft_dpi() {
            dpi / 96.
        } else {
            calc_dpi_factor(
                repr.size(),
                (
                    (*output_info).mm_width as u64,
                    (*output_info).mm_height as u64,
                ),
            )
        };

        (self.xrandr.XRRFreeOutputInfo)(output_info);
        Some((name, hidpi_factor, modes.collect()))
    }
}
<commit_msg>[#963] Change XRandR display size check to also take height into account (#964)<commit_after>use std::{env, slice, str::FromStr};

use super::*;
use crate::{dpi::validate_hidpi_factor, monitor::VideoMode};

pub fn calc_dpi_factor(
    (width_px, height_px): (u32, u32),
    (width_mm, height_mm): (u64, u64),
) -> f64 {
    // Override DPI if `WINIT_HIDPI_FACTOR` variable is set
    let dpi_override = env::var("WINIT_HIDPI_FACTOR")
        .ok()
        .and_then(|var| f64::from_str(&var).ok());
    if let Some(dpi_override) = dpi_override {
        if !validate_hidpi_factor(dpi_override) {
            panic!(
                "`WINIT_HIDPI_FACTOR` invalid; DPI factors must be normal floats greater than 0. Got `{}`",
                dpi_override,
            );
        }
        return dpi_override;
    }

    // See http://xpra.org/trac/ticket/728 for more information.
    if width_mm == 0 || height_mm == 0 {
        warn!("XRandR reported that the display's 0mm in size, which is certifiably insane");
        return 1.0;
    }

    let ppmm = ((width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)).sqrt();
    // Quantize 1/12 step size
    let dpi_factor = ((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0);
    assert!(validate_hidpi_factor(dpi_factor));
    dpi_factor
}

pub enum MonitorRepr {
    Monitor(*mut ffi::XRRMonitorInfo),
    Crtc(*mut ffi::XRRCrtcInfo),
}

impl MonitorRepr {
    pub unsafe fn get_output(&self) -> ffi::RROutput {
        match *self {
            // Same member names, but different locations within the struct...
            MonitorRepr::Monitor(monitor) => *((*monitor).outputs.offset(0)),
            MonitorRepr::Crtc(crtc) => *((*crtc).outputs.offset(0)),
        }
    }

    pub unsafe fn size(&self) -> (u32, u32) {
        match *self {
            MonitorRepr::Monitor(monitor) => ((*monitor).width as u32, (*monitor).height as u32),
            MonitorRepr::Crtc(crtc) => ((*crtc).width as u32, (*crtc).height as u32),
        }
    }

    pub unsafe fn position(&self) -> (i32, i32) {
        match *self {
            MonitorRepr::Monitor(monitor) => ((*monitor).x as i32, (*monitor).y as i32),
            MonitorRepr::Crtc(crtc) => ((*crtc).x as i32, (*crtc).y as i32),
        }
    }
}

impl From<*mut ffi::XRRMonitorInfo> for MonitorRepr {
    fn from(monitor: *mut ffi::XRRMonitorInfo) -> Self {
        MonitorRepr::Monitor(monitor)
    }
}

impl From<*mut ffi::XRRCrtcInfo> for MonitorRepr {
    fn from(crtc: *mut ffi::XRRCrtcInfo) -> Self {
        MonitorRepr::Crtc(crtc)
    }
}

impl XConnection {
    // Retrieve DPI from Xft.dpi property
    pub unsafe fn get_xft_dpi(&self) -> Option<f64> {
        (self.xlib.XrmInitialize)();
        let resource_manager_str = (self.xlib.XResourceManagerString)(self.display);
        if resource_manager_str == ptr::null_mut() {
            return None;
        }
        if let Ok(res) = ::std::ffi::CStr::from_ptr(resource_manager_str).to_str() {
            let name: &str = "Xft.dpi:\t";
            for pair in res.split("\n") {
                if pair.starts_with(&name) {
                    let res = &pair[name.len()..];
                    return f64::from_str(&res).ok();
                }
            }
        }
        None
    }
    pub unsafe fn get_output_info(
        &self,
        resources: *mut ffi::XRRScreenResources,
        repr: &MonitorRepr,
    ) -> Option<(String, f64, Vec<VideoMode>)> {
        let output_info =
            (self.xrandr.XRRGetOutputInfo)(self.display, resources, repr.get_output());
        if output_info.is_null() {
            // When calling `XRRGetOutputInfo` on a virtual monitor (versus a physical display)
            // it's possible for it to return null.
            // https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=816596
            let _ = self.check_errors(); // discard `BadRROutput` error
            return None;
        }

        let screen = (self.xlib.XDefaultScreen)(self.display);
        let bit_depth = (self.xlib.XDefaultDepth)(self.display, screen);

        let output_modes =
            slice::from_raw_parts((*output_info).modes, (*output_info).nmode as usize);
        let resource_modes = slice::from_raw_parts((*resources).modes, (*resources).nmode as usize);

        let modes = resource_modes
            .iter()
            // XRROutputInfo contains an array of mode ids that correspond to
            // modes in the array in XRRScreenResources
            .filter(|x| output_modes.iter().any(|id| x.id == *id))
            .map(|x| {
                let refresh_rate = if x.dotClock > 0 && x.hTotal > 0 && x.vTotal > 0 {
                    x.dotClock as u64 * 1000 / (x.hTotal as u64 * x.vTotal as u64)
                } else {
                    0
                };

                VideoMode {
                    size: (x.width, x.height),
                    refresh_rate: (refresh_rate as f32 / 1000.0).round() as u16,
                    bit_depth: bit_depth as u16,
                }
            });

        let name_slice = slice::from_raw_parts(
            (*output_info).name as *mut u8,
            (*output_info).nameLen as usize,
        );
        let name = String::from_utf8_lossy(name_slice).into();
        let hidpi_factor = if let Some(dpi) = self.get_xft_dpi() {
            dpi / 96.
        } else {
            calc_dpi_factor(
                repr.size(),
                (
                    (*output_info).mm_width as u64,
                    (*output_info).mm_height as u64,
                ),
            )
        };

        (self.xrandr.XRRFreeOutputInfo)(output_info);
        Some((name, hidpi_factor, modes.collect()))
    }
}
<|endoftext|>


<commit_before>use emailaddress::EmailAddress;
use iron::Url;
use serde_json::de::from_reader;
use serde_json::value::Value;
use super::error::{BrokerError, BrokerResult};
use super::hyper::client::Client as HttpClient;
use super::hyper::header::ContentType as HyContentType;
use super::hyper::header::Headers;
use super::{AppConfig, create_jwt};
use super::crypto::{session_id, verify_jws};
use super::store_cache::CacheKey;
use time::now_utc;
use url::percent_encoding::{utf8_percent_encode, QUERY_ENCODE_SET};

/// Helper method to issue an OAuth authorization request.
///
/// When an authentication request comes in and matches one of the "famous"
/// identity providers configured in the `AppConfig`, we redirect the client
/// to an Authentication Request URL, which we discover by reading the
/// provider's configured Discovery URL. We pass in the client ID we received
/// when pre-registering for the provider, as well as a callback URL which the
/// user will be redirected back to after confirming (or denying) the
/// Authentication Request. Included in the request is a nonce which we can
/// later use to definitively match the callback to this request.
pub fn request(app: &AppConfig, email_addr: EmailAddress, client_id: &str, nonce: &str, redirect_uri: &str)
               -> BrokerResult<Url> {

    let session = session_id(&email_addr, client_id);

    // Store the nonce and the RP's `redirect_uri` in Redis for use in the
    // callback handler.
    try!(app.store.store_session(&session, &[
        ("type", "oidc"),
        ("email", &email_addr.to_string()),
        ("client_id", client_id),
        ("nonce", nonce),
        ("redirect", redirect_uri),
    ]));

    let client = HttpClient::new();

    // Retrieve the provider's Discovery document and extract the
    // `authorization_endpoint` from it.
    let domain = &email_addr.domain;
    let provider = &app.providers[domain];
    let val: Value = try!(
        app.store.cache.fetch_json_url(
            &app.store,
            CacheKey::Discovery { domain: &email_addr.domain },
            &client,
            &provider.discovery
        )
    );
    let config = try!(
        val.as_object().ok_or_else(|| {
            BrokerError::Custom(format!("{} discovery document is not a JSON object", domain))
        })
    );
    let authz_base = try!(
        config.get("authorization_endpoint")
            .and_then(|v| v.as_str())
            .ok_or_else(|| {
                BrokerError::Custom(format!("{} authorization_endpoint is not a string", domain))
            })
    );

    // Create the URL to redirect to, properly escaping all parameters.
    Url::parse(&vec![
        authz_base,
        "?",
        "client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&response_type=code",
        "&scope=",
        &utf8_percent_encode("openid email", QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.base_url),
                             QUERY_ENCODE_SET).to_string(),
        "&state=",
        &utf8_percent_encode(&session, QUERY_ENCODE_SET).to_string(),
        "&login_hint=",
        &utf8_percent_encode(&email_addr.to_string(), QUERY_ENCODE_SET).to_string(),
    ].join("")).map_err(|_| {
        BrokerError::Custom("authorization_endpoint is an invalid URL".to_string())
    })

}

pub fn canonicalize_google(email: String) -> String {
    let at = email.find("@").unwrap();
    let (user, domain) = email.split_at(at);
    let domain = &domain[1..];
    let user = &user.replace(".", ""); // Ignore dots

    // Trim plus addresses
    let user = match user.find("+") {
        Some(pos) => user.split_at(pos).0,
        None => user,
    };

    // Normalize googlemail.com to gmail.com
    let domain = if domain == "googlemail.com" {
        "gmail.com"
    } else {
        domain
    };
    user.to_string() + "@" + domain
}

pub fn canonicalized(email: &str) -> String {
    let normalized = email.to_lowercase();
    if normalized.ends_with("@gmail.com") || normalized.ends_with("@googlemail.com") {
        canonicalize_google(normalized)
    } else {
        normalized
    }
}

/// Helper method to verify OAuth authentication result.
///
/// Match the returned email address and nonce against our Redis data, then
/// extract the identity token returned by the provider and verify it. Return
/// an identity token for the RP if successful, or an error message otherwise.
pub fn verify(app: &AppConfig, session: &str, code: &str)
              -> BrokerResult<(String, String)> {

    // Validate that the callback matches an auth request in Redis.
    let stored = try!(app.store.get_session(&session));
    if &stored["type"] != "oidc" {
        return Err(BrokerError::Custom("invalid session".to_string()));
    }

    let email_addr = EmailAddress::new(&stored["email"]).unwrap();
    let origin = &stored["client_id"];
    let nonce = &stored["nonce"];

    let client = HttpClient::new();

    // Request the provider's Discovery document to get the
    // `token_endpoint` and `jwks_uri` values from it. TODO: save these
    // when requesting the Discovery document in the `oauth_request()`
    // function, and/or cache them by provider host.
    let domain = &email_addr.domain;
    let provider = &app.providers[domain];
    let val: Value = try!(
        app.store.cache.fetch_json_url(
            &app.store,
            CacheKey::Discovery { domain: &email_addr.domain },
            &client,
            &provider.discovery
        )
    );
    let config = val.as_object().unwrap();
    let token_url = config["token_endpoint"].as_str().unwrap();

    // Create form data for the Token Request, where we exchange the code
    // received in this callback request for an identity token (while
    // proving our identity by passing the client secret value).
    let body: String = vec![
        "code=",
        &utf8_percent_encode(code, QUERY_ENCODE_SET).to_string(),
        "&client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&client_secret=",
        &utf8_percent_encode(&provider.secret, QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.base_url),
                             QUERY_ENCODE_SET).to_string(),
        "&grant_type=authorization_code",
    ].join("");

    // Send the Token Request and extract the `id_token` from the response.
    let token_obj: Value = {
        let mut headers = Headers::new();
        headers.set(HyContentType::form_url_encoded());
        let rsp = client.post(token_url).headers(headers).body(&body).send().unwrap();
        from_reader(rsp).unwrap()
    };
    let id_token = token_obj.find("id_token").unwrap().as_str().unwrap();

    // Grab the keys from the provider, then verify the signature.
    let jwt_payload = {
        let url = config["jwks_uri"].as_str().unwrap();
        let doc: Value = try!(
            app.store.cache.fetch_json_url(
                &app.store,
                CacheKey::KeySet { domain: &email_addr.domain },
                &client,
                &url
            )
        );
        verify_jws(id_token, &doc).unwrap()
    };

    // Verify that the issuer matches the configured value.
    let iss = jwt_payload.find("iss").unwrap().as_str().unwrap();
    let issuer_origin = vec!["https://", &provider.issuer].join("");
    assert!(iss == provider.issuer || iss == issuer_origin);

    // Verify the audience, subject, and expiry.
    let aud = jwt_payload.find("aud").unwrap().as_str().unwrap();
    assert!(aud == provider.client_id);
    let token_addr = jwt_payload.find("email").unwrap().as_str().unwrap();
    assert!(canonicalized(token_addr) == canonicalized(&email_addr.to_string()));
    let now = now_utc().to_timespec().sec;
    let exp = jwt_payload.find("exp").unwrap().as_i64().unwrap();
    assert!(now < exp);

    // If everything is okay, build a new identity token and send it
    // to the relying party.
    let id_token = create_jwt(app, &email_addr.to_string(), origin, nonce);
    let redirect = &stored["redirect"];
    Ok((id_token, redirect.to_string()))

}

#[cfg(test)]
mod tests {
    use super::canonicalized;
    #[test]
    fn test_canonicalized_google() {
        assert_eq!(canonicalized("example.foo+bar@example.com"),
                   "example.foo+bar@example.com");
        assert_eq!(canonicalized("example@gmail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example@googlemail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example.foo@gmail.com"),
                   "examplefoo@gmail.com");
        assert_eq!(canonicalized("example+bar@gmail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example.foo+bar@googlemail.com"),
                   "examplefoo@gmail.com");
    }
}
<commit_msg>Add a macro to retrieve JSON fields.<commit_after>use emailaddress::EmailAddress;
use iron::Url;
use serde_json::de::from_reader;
use serde_json::value::Value;
use super::error::{BrokerError, BrokerResult};
use super::hyper::client::Client as HttpClient;
use super::hyper::header::ContentType as HyContentType;
use super::hyper::header::Headers;
use super::{AppConfig, create_jwt};
use super::crypto::{session_id, verify_jws};
use super::store_cache::CacheKey;
use time::now_utc;
use url::percent_encoding::{utf8_percent_encode, QUERY_ENCODE_SET};

/// Macro used to extract a bunch of parameters from a JSON Value.
///
/// This macro will return from the contained method with a BrokerResult when
/// it fails to parse one of the params. The error will contain the description
/// string from the second parameter.
///
/// ```
/// extract_json_fields!(value, "example document", {
///     foo = as_str("foo"),
///     bar = as_i64("BAR"),
/// });
/// ```
macro_rules! extract_json_fields {
    ( $input:expr , $descr:expr , { $( $var:ident = $conv:ident ( $key:tt ) ),* } ) => {
        let descr = $descr;
        $(
            let $var = match $input.find($key).and_then(|v| v.$conv()) {
                None => {
                    return Err(BrokerError::Custom(format!("{} missing from {}", $key, descr)))
                },
                Some(value) => {
                    value
                }
            };
        )*
    }
}

/// Helper method to issue an OAuth authorization request.
///
/// When an authentication request comes in and matches one of the "famous"
/// identity providers configured in the `AppConfig`, we redirect the client
/// to an Authentication Request URL, which we discover by reading the
/// provider's configured Discovery URL. We pass in the client ID we received
/// when pre-registering for the provider, as well as a callback URL which the
/// user will be redirected back to after confirming (or denying) the
/// Authentication Request. Included in the request is a nonce which we can
/// later use to definitively match the callback to this request.
pub fn request(app: &AppConfig, email_addr: EmailAddress, client_id: &str, nonce: &str, redirect_uri: &str)
               -> BrokerResult<Url> {

    let session = session_id(&email_addr, client_id);

    // Store the nonce and the RP's `redirect_uri` in Redis for use in the
    // callback handler.
    try!(app.store.store_session(&session, &[
        ("type", "oidc"),
        ("email", &email_addr.to_string()),
        ("client_id", client_id),
        ("nonce", nonce),
        ("redirect", redirect_uri),
    ]));

    let client = HttpClient::new();

    // Retrieve the provider's Discovery document and extract the
    // `authorization_endpoint` from it.
    let domain = &email_addr.domain;
    let provider = &app.providers[domain];
    let config_obj: Value = try!(
        app.store.cache.fetch_json_url(
            &app.store,
            CacheKey::Discovery { domain: &email_addr.domain },
            &client,
            &provider.discovery
        )
    );
    extract_json_fields!(config_obj, format!("{} discovery document", domain), {
        authz_base = as_str("authorization_endpoint")
    });

    // Create the URL to redirect to, properly escaping all parameters.
    Url::parse(&vec![
        authz_base,
        "?",
        "client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&response_type=code",
        "&scope=",
        &utf8_percent_encode("openid email", QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.base_url),
                             QUERY_ENCODE_SET).to_string(),
        "&state=",
        &utf8_percent_encode(&session, QUERY_ENCODE_SET).to_string(),
        "&login_hint=",
        &utf8_percent_encode(&email_addr.to_string(), QUERY_ENCODE_SET).to_string(),
    ].join("")).map_err(|_| {
        BrokerError::Custom("authorization_endpoint is an invalid URL".to_string())
    })

}

pub fn canonicalize_google(email: String) -> String {
    let at = email.find("@").unwrap();
    let (user, domain) = email.split_at(at);
    let domain = &domain[1..];
    let user = &user.replace(".", ""); // Ignore dots

    // Trim plus addresses
    let user = match user.find("+") {
        Some(pos) => user.split_at(pos).0,
        None => user,
    };

    // Normalize googlemail.com to gmail.com
    let domain = if domain == "googlemail.com" {
        "gmail.com"
    } else {
        domain
    };
    user.to_string() + "@" + domain
}

pub fn canonicalized(email: &str) -> String {
    let normalized = email.to_lowercase();
    if normalized.ends_with("@gmail.com") || normalized.ends_with("@googlemail.com") {
        canonicalize_google(normalized)
    } else {
        normalized
    }
}

/// Helper method to verify OAuth authentication result.
///
/// Match the returned email address and nonce against our Redis data, then
/// extract the identity token returned by the provider and verify it. Return
/// an identity token for the RP if successful, or an error message otherwise.
pub fn verify(app: &AppConfig, session: &str, code: &str)
              -> BrokerResult<(String, String)> {

    // Validate that the callback matches an auth request in Redis.
    let stored = try!(app.store.get_session(&session));
    if &stored["type"] != "oidc" {
        return Err(BrokerError::Custom("invalid session".to_string()));
    }

    let email_addr = EmailAddress::new(&stored["email"]).unwrap();
    let origin = &stored["client_id"];
    let nonce = &stored["nonce"];

    let client = HttpClient::new();

    // Request the provider's Discovery document to get the
    // `token_endpoint` and `jwks_uri` values from it. TODO: save these
    // when requesting the Discovery document in the `oauth_request()`
    // function, and/or cache them by provider host.
    let domain = &email_addr.domain;
    let provider = &app.providers[domain];
    let config_obj: Value = try!(
        app.store.cache.fetch_json_url(
            &app.store,
            CacheKey::Discovery { domain: &email_addr.domain },
            &client,
            &provider.discovery
        )
    );
    extract_json_fields!(config_obj, format!("{} discovery document", domain), {
        token_url = as_str("token_endpoint"),
        jwks_url = as_str("jwks_uri")
    });

    // Create form data for the Token Request, where we exchange the code
    // received in this callback request for an identity token (while
    // proving our identity by passing the client secret value).
    let body: String = vec![
        "code=",
        &utf8_percent_encode(code, QUERY_ENCODE_SET).to_string(),
        "&client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&client_secret=",
        &utf8_percent_encode(&provider.secret, QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.base_url),
                             QUERY_ENCODE_SET).to_string(),
        "&grant_type=authorization_code",
    ].join("");

    // Send the Token Request and extract the `id_token` from the response.
    let token_obj: Value = {
        let mut headers = Headers::new();
        headers.set(HyContentType::form_url_encoded());
        let rsp = client.post(token_url).headers(headers).body(&body).send().unwrap();
        from_reader(rsp).unwrap()
    };
    extract_json_fields!(token_obj, format!("{} token response", domain), {
        id_token = as_str("id_token")
    });

    // Grab the keys from the provider, then verify the signature.
    let jwt_payload = {
        let keys_obj: Value = try!(
            app.store.cache.fetch_json_url(
                &app.store,
                CacheKey::KeySet { domain: &email_addr.domain },
                &client,
                &jwks_url
            )
        );
        verify_jws(id_token, &keys_obj).unwrap()
    };

    // Verify the claims contained in the token.
    extract_json_fields!(jwt_payload, format!("{} token", domain), {
        iss = as_str("iss"),
        aud = as_str("aud"),
        token_addr = as_str("email"),
        exp = as_i64("exp")
    });
    let issuer_origin = vec!["https://", &provider.issuer].join("");
    assert!(iss == provider.issuer || iss == issuer_origin);
    assert!(aud == provider.client_id);
    assert!(canonicalized(token_addr) == canonicalized(&email_addr.to_string()));
    let now = now_utc().to_timespec().sec;
    assert!(now < exp);

    // If everything is okay, build a new identity token and send it
    // to the relying party.
    let id_token = create_jwt(app, &email_addr.to_string(), origin, nonce);
    let redirect = &stored["redirect"];
    Ok((id_token, redirect.to_string()))

}

#[cfg(test)]
mod tests {
    use super::canonicalized;
    #[test]
    fn test_canonicalized_google() {
        assert_eq!(canonicalized("example.foo+bar@example.com"),
                   "example.foo+bar@example.com");
        assert_eq!(canonicalized("example@gmail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example@googlemail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example.foo@gmail.com"),
                   "examplefoo@gmail.com");
        assert_eq!(canonicalized("example+bar@gmail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example.foo+bar@googlemail.com"),
                   "examplefoo@gmail.com");
    }
}
<|endoftext|>


<commit_before>use std::convert::TryFrom;
use std::io::{self, Cursor, Read};
use std::marker::PhantomData;
use std::mem;

use crate::color::ColorType;
use crate::image::ImageDecoder;
use crate::error::{ImageError, ImageResult};

/// JPEG decoder
pub struct JpegDecoder<R> {
    decoder: jpeg::Decoder<R>,
    metadata: jpeg::ImageInfo,
}

impl<R: Read> JpegDecoder<R> {
    /// Create a new decoder that decodes from the stream ```r```
    pub fn new(r: R) -> ImageResult<JpegDecoder<R>> {
        let mut decoder = jpeg::Decoder::new(r);

        decoder.read_info().map_err(ImageError::from_jpeg)?;
        let mut metadata = decoder.info().unwrap();

        // We convert CMYK data to RGB before returning it to the user.
        if metadata.pixel_format == jpeg::PixelFormat::CMYK32 {
            metadata.pixel_format = jpeg::PixelFormat::RGB24;
        }

        Ok(JpegDecoder {
            decoder,
            metadata,
        })
    }
}

/// Wrapper struct around a `Cursor<Vec<u8>>`
pub struct JpegReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
impl<R> Read for JpegReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        if self.0.position() == 0 && buf.is_empty() {
            mem::swap(buf, self.0.get_mut());
            Ok(buf.len())
        } else {
            self.0.read_to_end(buf)
        }
    }
}

impl<'a, R: 'a + Read> ImageDecoder<'a> for JpegDecoder<R> {
    type Reader = JpegReader<R>;

    fn dimensions(&self) -> (u32, u32) {
        (u32::from(self.metadata.width), u32::from(self.metadata.height))
    }

    fn color_type(&self) -> ColorType {
        ColorType::from_jpeg(self.metadata.pixel_format)
    }

    fn into_reader(mut self) -> ImageResult<Self::Reader> {
        let mut data = self.decoder.decode().map_err(ImageError::from_jpeg)?;
        data = match self.decoder.info().unwrap().pixel_format {
            jpeg::PixelFormat::CMYK32 => cmyk_to_rgb(&data),
            _ => data,
        };

        Ok(JpegReader(Cursor::new(data), PhantomData))
    }

    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));

        let mut data = self.decoder.decode().map_err(ImageError::from_jpeg)?;
        data = match self.decoder.info().unwrap().pixel_format {
            jpeg::PixelFormat::CMYK32 => cmyk_to_rgb(&data),
            _ => data,
        };

        buf.copy_from_slice(&data);
        Ok(())
    }
}

pub fn cmyk_to_rgb(input: &[u8]) -> Vec<u8> {
    let count = input.len()/4;
    let mut output = vec![0; 3*count];

    let in_pixels = input[..4*count].chunks_exact(4);
    let out_pixels = output[..3*count].chunks_exact_mut(3);

    let inv = 1.0/255.0;
    for (pixel, outp) in in_pixels.zip(out_pixels) {
        let c = 255 - u16::from(pixel[0]);
        let m = 255 - u16::from(pixel[1]);
        let y = 255 - u16::from(pixel[2]);
        let k = 255 - u16::from(pixel[3]);

        // CMY -> RGB
        let r = (k * c) as f32 * inv;
        let g = (k * m) as f32 * inv;
        let b = (k * y) as f32 * inv;

        outp[0] = r as u8;
        outp[1] = g as u8;
        outp[2] = b as u8;
    }

    output
}

impl ColorType {
    fn from_jpeg(pixel_format: jpeg::PixelFormat) -> ColorType {
        use jpeg::PixelFormat::*;
        match pixel_format {
            L8 => ColorType::L8,
            RGB24 => ColorType::Rgb8,
            CMYK32 => panic!(),
        }
    }
}

impl ImageError {
    fn from_jpeg(err: jpeg::Error) -> ImageError {
        use jpeg::Error::*;
        match err {
            Format(desc) => ImageError::FormatError(desc),
            Unsupported(desc) => ImageError::UnsupportedError(format!("{:?}", desc)),
            Io(err) => ImageError::IoError(err),
            Internal(err) => ImageError::FormatError(err.to_string()),
        }
    }
}

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

    #[test]
    fn cymk_to_rgb_correct() {
        // Based on R = 255 * (1-C/255) * (1-K/255)
        for c in 0..255 {
            for k in 0..255 {
                let r = (255.0 - f32::from(c)) * (255.0 - f32::from(k)) / 255.0;
                let r_u8 = r as u8;
                let convert_r = cmyk_to_rgb(&[c, 0, 0, k])[0];
                assert_eq!(convert_r, r_u8,
                           "c = {}, k = {}, cymk_to_rgb[0] = {}, should be {}", c, k, convert_r, r_u8);
            }
        }
    }
}

<commit_msg>Test all channels independently.<commit_after>use std::convert::TryFrom;
use std::io::{self, Cursor, Read};
use std::marker::PhantomData;
use std::mem;

use crate::color::ColorType;
use crate::image::ImageDecoder;
use crate::error::{ImageError, ImageResult};

/// JPEG decoder
pub struct JpegDecoder<R> {
    decoder: jpeg::Decoder<R>,
    metadata: jpeg::ImageInfo,
}

impl<R: Read> JpegDecoder<R> {
    /// Create a new decoder that decodes from the stream ```r```
    pub fn new(r: R) -> ImageResult<JpegDecoder<R>> {
        let mut decoder = jpeg::Decoder::new(r);

        decoder.read_info().map_err(ImageError::from_jpeg)?;
        let mut metadata = decoder.info().unwrap();

        // We convert CMYK data to RGB before returning it to the user.
        if metadata.pixel_format == jpeg::PixelFormat::CMYK32 {
            metadata.pixel_format = jpeg::PixelFormat::RGB24;
        }

        Ok(JpegDecoder {
            decoder,
            metadata,
        })
    }
}

/// Wrapper struct around a `Cursor<Vec<u8>>`
pub struct JpegReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
impl<R> Read for JpegReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        if self.0.position() == 0 && buf.is_empty() {
            mem::swap(buf, self.0.get_mut());
            Ok(buf.len())
        } else {
            self.0.read_to_end(buf)
        }
    }
}

impl<'a, R: 'a + Read> ImageDecoder<'a> for JpegDecoder<R> {
    type Reader = JpegReader<R>;

    fn dimensions(&self) -> (u32, u32) {
        (u32::from(self.metadata.width), u32::from(self.metadata.height))
    }

    fn color_type(&self) -> ColorType {
        ColorType::from_jpeg(self.metadata.pixel_format)
    }

    fn into_reader(mut self) -> ImageResult<Self::Reader> {
        let mut data = self.decoder.decode().map_err(ImageError::from_jpeg)?;
        data = match self.decoder.info().unwrap().pixel_format {
            jpeg::PixelFormat::CMYK32 => cmyk_to_rgb(&data),
            _ => data,
        };

        Ok(JpegReader(Cursor::new(data), PhantomData))
    }

    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));

        let mut data = self.decoder.decode().map_err(ImageError::from_jpeg)?;
        data = match self.decoder.info().unwrap().pixel_format {
            jpeg::PixelFormat::CMYK32 => cmyk_to_rgb(&data),
            _ => data,
        };

        buf.copy_from_slice(&data);
        Ok(())
    }
}

pub fn cmyk_to_rgb(input: &[u8]) -> Vec<u8> {
    let count = input.len()/4;
    let mut output = vec![0; 3*count];

    let in_pixels = input[..4*count].chunks_exact(4);
    let out_pixels = output[..3*count].chunks_exact_mut(3);

    let inv = 1.0/255.0;
    for (pixel, outp) in in_pixels.zip(out_pixels) {
        let c = 255 - u16::from(pixel[0]);
        let m = 255 - u16::from(pixel[1]);
        let y = 255 - u16::from(pixel[2]);
        let k = 255 - u16::from(pixel[3]);

        // CMY -> RGB
        let r = (k * c) as f32 * inv;
        let g = (k * m) as f32 * inv;
        let b = (k * y) as f32 * inv;

        outp[0] = r as u8;
        outp[1] = g as u8;
        outp[2] = b as u8;
    }

    output
}

impl ColorType {
    fn from_jpeg(pixel_format: jpeg::PixelFormat) -> ColorType {
        use jpeg::PixelFormat::*;
        match pixel_format {
            L8 => ColorType::L8,
            RGB24 => ColorType::Rgb8,
            CMYK32 => panic!(),
        }
    }
}

impl ImageError {
    fn from_jpeg(err: jpeg::Error) -> ImageError {
        use jpeg::Error::*;
        match err {
            Format(desc) => ImageError::FormatError(desc),
            Unsupported(desc) => ImageError::UnsupportedError(format!("{:?}", desc)),
            Io(err) => ImageError::IoError(err),
            Internal(err) => ImageError::FormatError(err.to_string()),
        }
    }
}

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

    #[test]
    fn cymk_to_rgb_correct() {
        // Based on R = 255 * (1-C/255) * (1-K/255)
        for c in 0..255 {
            for k in 0..255 {
                let r = (255.0 - f32::from(c)) * (255.0 - f32::from(k)) / 255.0;
                let r_u8 = r as u8;
                let convert_r = cmyk_to_rgb(&[c, 0, 0, k])[0];
                let convert_g = cmyk_to_rgb(&[0, c, 0, k])[0];
                let convert_b = cmyk_to_rgb(&[0, 0, c, k])[0];

                assert_eq!(convert_r, r_u8,
                           "c = {}, k = {}, cymk_to_rgb[0] = {}, should be {}", c, k, convert_r, r_u8);
                assert_eq!(convert_g, r_u8,
                           "m = {}, k = {}, cymk_to_rgb[0] = {}, should be {}", c, k, convert_g, r_u8);
                assert_eq!(convert_b, r_u8,
                           "y = {}, k = {}, cymk_to_rgb[0] = {}, should be {}", c, k, convert_b, r_u8);
            }
        }
    }
}

<|endoftext|>