<commit_before>use crate::remote::RemoteStatus;
use crate::tui::state::ProgressTime;
use crate::tui::state::SharedState;
use crate::tui::{state::StateEvent, UIState};
use crate::util;
use crate::{
    series::{LoadedSeries, Series},
    tui::component::Component,
};
use anime::remote::{ScoreParser, SeriesDate};
use chrono::Utc;
use smallvec::{smallvec, SmallVec};
use std::{
    borrow::Cow,
    sync::{atomic::Ordering, Arc},
    time::Duration,
};
use std::{fmt, sync::atomic::AtomicU32};
use tokio::task;
use tui::backend::Backend;
use tui::layout::{Alignment, Direction, Rect};
use tui::style::Color;
use tui::terminal::Frame;
use tui::text::Span;
use tui_utils::{
    helpers::{block, text},
    layout::{BasicConstraint, RectExt, SimpleLayout},
    widgets::{Fragment, OverflowMode, SimpleText, SpanOptions, TextFragments},
    wrap,
};
use util::ScopedTask;

pub struct InfoPanel {
    progress_remaining_secs: Arc<AtomicU32>,
    #[allow(dead_code)]
    event_monitor_task: ScopedTask<()>,
}

impl InfoPanel {
    pub fn new(state: &SharedState) -> Self {
        let progress_remaining_secs = Arc::new(AtomicU32::default());
        let event_monitor_task =
            Self::spawn_episode_event_monitor(state, Arc::clone(&progress_remaining_secs)).into();

        Self {
            progress_remaining_secs,
            event_monitor_task,
        }
    }

    fn spawn_episode_event_monitor(
        state: &SharedState,
        progress_remaining_secs: Arc<AtomicU32>,
    ) -> task::JoinHandle<()> {
        let state = state.clone();

        task::spawn(async move {
            let mut events = {
                let state = state.lock();
                state.events.subscribe()
            };

            #[allow(unused_variables)]
            let mut progress_task: Option<ScopedTask<_>> = None;

            while let Ok(event) = events.recv().await {
                #[allow(unused_assignments)]
                match event {
                    StateEvent::StartedEpisode(progress_time) => {
                        let state = state.clone();
                        let remaining_secs = Arc::clone(&progress_remaining_secs);

                        let task =
                            Self::spawn_progress_monitor_task(state, remaining_secs, progress_time)
                                .into();

                        progress_task = Some(task);
                    }
                    StateEvent::FinishedEpisode => {
                        progress_remaining_secs.store(0, Ordering::SeqCst);
                        progress_task = None;

                        let mut state = state.lock();
                        state.mark_dirty();
                    }
                }
            }
        })
    }

    fn spawn_progress_monitor_task(
        state: SharedState,
        remaining_secs: Arc<AtomicU32>,
        progress_time: ProgressTime,
    ) -> task::JoinHandle<()> {
        task::spawn(async move {
            let mut first_iter = true;

            loop {
                let cur_progress = remaining_secs.load(Ordering::SeqCst);

                if cur_progress == 0 && !first_iter {
                    remaining_secs.store(0, Ordering::SeqCst);
                    state.lock().mark_dirty();
                    break;
                }

                let diff = progress_time - Utc::now();
                let secs = diff.num_seconds();

                if secs <= 0 {
                    remaining_secs.store(0, Ordering::SeqCst);
                    state.lock().mark_dirty();
                    break;
                }

                remaining_secs.store(secs as u32, Ordering::SeqCst);
                first_iter = false;

                {
                    state.lock().mark_dirty();
                }

                tokio::time::sleep(Duration::from_secs(60)).await;
            }
        })
    }

    fn header_body_layout(rect: Rect) -> (Rect, Rect) {
        let layout = SimpleLayout::new(Direction::Vertical).margin(2).split(
            rect,
            &[BasicConstraint::Length(2), BasicConstraint::Percentage(100)],
        );

        (layout[0], layout[1])
    }

    fn draw_text_panel<B>(
        header: Span,
        body: &[Fragment],
        header_pos: Rect,
        body_pos: Rect,
        frame: &mut Frame<B>,
    ) where
        B: Backend,
    {
        let header_widget = SimpleText::new(header)
            .alignment(Alignment::Center)
            .overflow(OverflowMode::Truncate);

        frame.render_widget(header_widget, header_pos);

        let body_widget = TextFragments::new(body).alignment(Alignment::Center);
        frame.render_widget(body_widget, body_pos);
    }

    fn draw_no_users_info<B>(rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let span = |text| {
            Fragment::Span(
                Span::raw(text),
                SpanOptions::new().overflow(OverflowMode::Truncate),
            )
        };

        let body = [
            span("Add an account by pressing 'u' to open"),
            Fragment::Line,
            span("user management and then by pressing tab"),
            Fragment::Line,
            span("to switch to the add user panel."),
            Fragment::Line,
            Fragment::Line,
            span("Then open the auth URL in your browser"),
            Fragment::Line,
            span("by pressing Ctrl + O, and follow its instructions."),
            Fragment::Line,
            Fragment::Line,
            span("Once you have a token, paste it in with either"),
            Fragment::Line,
            span("Ctrl + Shift + V or Ctrl + V."),
            Fragment::Line,
            Fragment::Line,
            span("More detailed instructions here:"),
            Fragment::Line,
            span("https://github.com/Acizza/anup#adding-an-account"),
        ];

        let (h_pos, b_pos) = Self::header_body_layout(rect);
        Self::draw_text_panel(text::bold("No Accounts Added"), &body, h_pos, b_pos, frame);
    }

    fn draw_no_series_found<B>(rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let span = |text| {
            Fragment::Span(
                Span::raw(text),
                SpanOptions::new().overflow(OverflowMode::Truncate),
            )
        };

        let body = [
            span("Add one by pressing the 'a' key."),
            Fragment::Line,
            Fragment::Line,
            span("The opened panel will require you to specify"),
            Fragment::Line,
            span("a name for the series you want to add."),
            Fragment::Line,
            Fragment::Line,
            span("For automatic detection, the name should be"),
            Fragment::Line,
            span("similar to the name of the folder the series"),
            Fragment::Line,
            span("is in on disk."),
        ];

        let (h_pos, b_pos) = Self::header_body_layout(rect);
        Self::draw_text_panel(text::bold("No Series Found"), &body, h_pos, b_pos, frame);
    }

    fn draw_series_error<B, E>(err: E, rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
        E: fmt::Display,
    {
        let header = text::bold_with("Error Loading Series", |s| s.fg(Color::Red));

        let body = [Fragment::Span(
            text::with_color(err.to_string(), Color::Red),
            SpanOptions::new().overflow(OverflowMode::Truncate),
        )];

        let (h_pos, b_pos) = Self::header_body_layout(rect);
        let wrapped = wrap::by_letters(IntoIter::new(body), b_pos.width);

        Self::draw_text_panel(header, &wrapped, h_pos, b_pos, frame);
    }

    #[allow(clippy::too_many_lines)]
    fn draw_series_info<B>(
        &self,
        state: &UIState,
        series: &Series,
        rect: Rect,
        frame: &mut Frame<B>,
    ) where
        B: Backend,
    {
        let layout = SimpleLayout::new(Direction::Vertical).margin(2).split(
            rect,
            &[
                BasicConstraint::Length(4),
                BasicConstraint::Percentage(70),
                BasicConstraint::Length(4),
            ],
        );

        let info = &series.data.info;
        let entry = &series.data.entry;

        // Series title
        {
            let mut fragments: SmallVec<[Fragment; 2]> = smallvec![Fragment::Span(
                text::bold(&info.title_preferred),
                SpanOptions::new().overflow(OverflowMode::Truncate)
            )];

            if entry.needs_sync() {
                fragments.push(Fragment::span(text::italic(" [*]")));
            }

            let title_widget = TextFragments::new(&fragments).alignment(Alignment::Center);
            frame.render_widget(title_widget, layout[0]);
        }

        // Items in panel

        macro_rules! draw_stat {
            ($x_column:expr, $y_column:expr => $header:expr, $value:expr) => {{
                let content = layout[1];

                let pos = content.grid_pos(Rect {
                    x: $x_column,
                    y: $y_column,
                    width: content.width / 3,
                    height: content.height / 3,
                });

                Self::draw_stat($header, $value, pos, frame);
            }};
        }

        // Left panel items

        draw_stat!(0, 0 => "Watch Time", {
            let watch_time_mins = info.episodes * info.episode_length_mins;
            util::hm_from_mins(f32::from(watch_time_mins))
        });

        draw_stat!(0, 1 => "Time Left", {
            let eps_left = info.episodes - entry.watched_episodes().min(info.episodes);
            let time_left_mins = eps_left * info.episode_length_mins;
            util::hm_from_mins(f32::from(time_left_mins))
        });

        draw_stat!(0, 2 => "Episode Length", format!("{}M", info.episode_length_mins));

        // Middle panel items

        draw_stat!(1, 0 => "Progress", format!("{}|{}", entry.watched_episodes(), info.episodes));

        draw_stat!(1, 1 => "Score", {
            match (entry.score(), &state.remote) {
                (Some(score), RemoteStatus::LoggedIn(remote)) => remote.score_to_str(score as u8),
                (Some(score), RemoteStatus::LoggingIn(_)) => score.to_string().into(),
                (None, _) => "??".into(),
            }
        });

        draw_stat!(1, 2 => "Status", {
            let status: &'static str = entry.status().into();
            status
        });

        // Right panel items

        // TODO: allow the format to be changed in the config
        let format_date = |date: Option<SeriesDate>| {
            date.map_or_else(
                || Cow::Borrowed("??"),
                |date| format!("{:02}/{:02}/{:02}", date.month, date.day, date.year % 100).into(),
            )
        };

        draw_stat!(2, 0 => "Start Date", format_date(entry.start_date()));
        draw_stat!(2, 1 => "Finish Date", format_date(entry.end_date()));
        draw_stat!(2, 2 => "Rewatched", entry.times_rewatched().to_string());

        self.draw_status_text(state, layout[2], frame);
    }

    fn draw_stat<B, S>(header: &str, value: S, rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
        S: AsRef<str>,
    {
        let fragments = [
            Fragment::span(text::bold(header)),
            Fragment::Line,
            Fragment::span(text::italic(value.as_ref())),
        ];

        let widget = TextFragments::new(&fragments).alignment(Alignment::Center);
        frame.render_widget(widget, rect);
    }

    fn draw_status_text<B: Backend>(&self, state: &UIState, rect: Rect, frame: &mut Frame<B>) {
        let progress_remaining_secs = self.progress_remaining_secs.load(Ordering::SeqCst);

        // Remaining time until progression
        if progress_remaining_secs > 0 {
            let mins = (progress_remaining_secs as f32 / 60.0).round() as u32;

            let fragments = [
                Fragment::span(text::bold(mins.to_string())),
                Fragment::span(text::bold(" Minutes Until Progression")),
            ];

            let widget = TextFragments::new(&fragments).alignment(Alignment::Center);
            frame.render_widget(widget, rect);
        }
        // Login message
        else if let RemoteStatus::LoggingIn(username) = &state.remote {
            let fragments = [
                Fragment::span(text::bold("Logging In As ")),
                Fragment::Span(
                    text::bold_with(username, |s| s.fg(Color::Blue)),
                    SpanOptions::new().overflow(OverflowMode::Truncate),
                ),
            ];

            let widget = TextFragments::new(&fragments).alignment(Alignment::Center);
            frame.render_widget(widget, rect);
        }
    }

    pub fn draw<B: Backend>(&mut self, state: &UIState, rect: Rect, frame: &mut Frame<B>) {
        let info_block = block::with_borders("Info");
        frame.render_widget(info_block, rect);

        if state.users.get().is_empty() {
            return Self::draw_no_users_info(rect, frame);
        }

        match state.series.selected() {
            Some(LoadedSeries::Complete(series)) => {
                self.draw_series_info(state, series, rect, frame)
            }
            Some(LoadedSeries::Partial(_, err)) => Self::draw_series_error(err, rect, frame),
            Some(LoadedSeries::None(_, err)) => Self::draw_series_error(err, rect, frame),
            None => Self::draw_no_series_found(rect, frame),
        }
    }
}

impl Component for InfoPanel {
    type State = ();
    type KeyResult = ();

    fn process_key(&mut self, _: crate::key::Key, _: &mut Self::State) -> Self::KeyResult {}
}
<commit_msg>anup: Fix build error by importing std::array::IntoIter in info panel<commit_after>use crate::remote::RemoteStatus;
use crate::tui::state::ProgressTime;
use crate::tui::state::SharedState;
use crate::tui::{state::StateEvent, UIState};
use crate::util;
use crate::{
    series::{LoadedSeries, Series},
    tui::component::Component,
};
use anime::remote::{ScoreParser, SeriesDate};
use chrono::Utc;
use smallvec::{smallvec, SmallVec};
use std::{
    array::IntoIter,
    borrow::Cow,
    sync::{atomic::Ordering, Arc},
    time::Duration,
};
use std::{fmt, sync::atomic::AtomicU32};
use tokio::task;
use tui::backend::Backend;
use tui::layout::{Alignment, Direction, Rect};
use tui::style::Color;
use tui::terminal::Frame;
use tui::text::Span;
use tui_utils::{
    helpers::{block, text},
    layout::{BasicConstraint, RectExt, SimpleLayout},
    widgets::{Fragment, OverflowMode, SimpleText, SpanOptions, TextFragments},
    wrap,
};
use util::ScopedTask;

pub struct InfoPanel {
    progress_remaining_secs: Arc<AtomicU32>,
    #[allow(dead_code)]
    event_monitor_task: ScopedTask<()>,
}

impl InfoPanel {
    pub fn new(state: &SharedState) -> Self {
        let progress_remaining_secs = Arc::new(AtomicU32::default());
        let event_monitor_task =
            Self::spawn_episode_event_monitor(state, Arc::clone(&progress_remaining_secs)).into();

        Self {
            progress_remaining_secs,
            event_monitor_task,
        }
    }

    fn spawn_episode_event_monitor(
        state: &SharedState,
        progress_remaining_secs: Arc<AtomicU32>,
    ) -> task::JoinHandle<()> {
        let state = state.clone();

        task::spawn(async move {
            let mut events = {
                let state = state.lock();
                state.events.subscribe()
            };

            #[allow(unused_variables)]
            let mut progress_task: Option<ScopedTask<_>> = None;

            while let Ok(event) = events.recv().await {
                #[allow(unused_assignments)]
                match event {
                    StateEvent::StartedEpisode(progress_time) => {
                        let state = state.clone();
                        let remaining_secs = Arc::clone(&progress_remaining_secs);

                        let task =
                            Self::spawn_progress_monitor_task(state, remaining_secs, progress_time)
                                .into();

                        progress_task = Some(task);
                    }
                    StateEvent::FinishedEpisode => {
                        progress_remaining_secs.store(0, Ordering::SeqCst);
                        progress_task = None;

                        let mut state = state.lock();
                        state.mark_dirty();
                    }
                }
            }
        })
    }

    fn spawn_progress_monitor_task(
        state: SharedState,
        remaining_secs: Arc<AtomicU32>,
        progress_time: ProgressTime,
    ) -> task::JoinHandle<()> {
        task::spawn(async move {
            let mut first_iter = true;

            loop {
                let cur_progress = remaining_secs.load(Ordering::SeqCst);

                if cur_progress == 0 && !first_iter {
                    remaining_secs.store(0, Ordering::SeqCst);
                    state.lock().mark_dirty();
                    break;
                }

                let diff = progress_time - Utc::now();
                let secs = diff.num_seconds();

                if secs <= 0 {
                    remaining_secs.store(0, Ordering::SeqCst);
                    state.lock().mark_dirty();
                    break;
                }

                remaining_secs.store(secs as u32, Ordering::SeqCst);
                first_iter = false;

                {
                    state.lock().mark_dirty();
                }

                tokio::time::sleep(Duration::from_secs(60)).await;
            }
        })
    }

    fn header_body_layout(rect: Rect) -> (Rect, Rect) {
        let layout = SimpleLayout::new(Direction::Vertical).margin(2).split(
            rect,
            &[BasicConstraint::Length(2), BasicConstraint::Percentage(100)],
        );

        (layout[0], layout[1])
    }

    fn draw_text_panel<B>(
        header: Span,
        body: &[Fragment],
        header_pos: Rect,
        body_pos: Rect,
        frame: &mut Frame<B>,
    ) where
        B: Backend,
    {
        let header_widget = SimpleText::new(header)
            .alignment(Alignment::Center)
            .overflow(OverflowMode::Truncate);

        frame.render_widget(header_widget, header_pos);

        let body_widget = TextFragments::new(body).alignment(Alignment::Center);
        frame.render_widget(body_widget, body_pos);
    }

    fn draw_no_users_info<B>(rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let span = |text| {
            Fragment::Span(
                Span::raw(text),
                SpanOptions::new().overflow(OverflowMode::Truncate),
            )
        };

        let body = [
            span("Add an account by pressing 'u' to open"),
            Fragment::Line,
            span("user management and then by pressing tab"),
            Fragment::Line,
            span("to switch to the add user panel."),
            Fragment::Line,
            Fragment::Line,
            span("Then open the auth URL in your browser"),
            Fragment::Line,
            span("by pressing Ctrl + O, and follow its instructions."),
            Fragment::Line,
            Fragment::Line,
            span("Once you have a token, paste it in with either"),
            Fragment::Line,
            span("Ctrl + Shift + V or Ctrl + V."),
            Fragment::Line,
            Fragment::Line,
            span("More detailed instructions here:"),
            Fragment::Line,
            span("https://github.com/Acizza/anup#adding-an-account"),
        ];

        let (h_pos, b_pos) = Self::header_body_layout(rect);
        Self::draw_text_panel(text::bold("No Accounts Added"), &body, h_pos, b_pos, frame);
    }

    fn draw_no_series_found<B>(rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
    {
        let span = |text| {
            Fragment::Span(
                Span::raw(text),
                SpanOptions::new().overflow(OverflowMode::Truncate),
            )
        };

        let body = [
            span("Add one by pressing the 'a' key."),
            Fragment::Line,
            Fragment::Line,
            span("The opened panel will require you to specify"),
            Fragment::Line,
            span("a name for the series you want to add."),
            Fragment::Line,
            Fragment::Line,
            span("For automatic detection, the name should be"),
            Fragment::Line,
            span("similar to the name of the folder the series"),
            Fragment::Line,
            span("is in on disk."),
        ];

        let (h_pos, b_pos) = Self::header_body_layout(rect);
        Self::draw_text_panel(text::bold("No Series Found"), &body, h_pos, b_pos, frame);
    }

    fn draw_series_error<B, E>(err: E, rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
        E: fmt::Display,
    {
        let header = text::bold_with("Error Loading Series", |s| s.fg(Color::Red));

        let body = [Fragment::Span(
            text::with_color(err.to_string(), Color::Red),
            SpanOptions::new().overflow(OverflowMode::Truncate),
        )];

        let (h_pos, b_pos) = Self::header_body_layout(rect);
        let wrapped = wrap::by_letters(IntoIter::new(body), b_pos.width);

        Self::draw_text_panel(header, &wrapped, h_pos, b_pos, frame);
    }

    #[allow(clippy::too_many_lines)]
    fn draw_series_info<B>(
        &self,
        state: &UIState,
        series: &Series,
        rect: Rect,
        frame: &mut Frame<B>,
    ) where
        B: Backend,
    {
        let layout = SimpleLayout::new(Direction::Vertical).margin(2).split(
            rect,
            &[
                BasicConstraint::Length(4),
                BasicConstraint::Percentage(70),
                BasicConstraint::Length(4),
            ],
        );

        let info = &series.data.info;
        let entry = &series.data.entry;

        // Series title
        {
            let mut fragments: SmallVec<[Fragment; 2]> = smallvec![Fragment::Span(
                text::bold(&info.title_preferred),
                SpanOptions::new().overflow(OverflowMode::Truncate)
            )];

            if entry.needs_sync() {
                fragments.push(Fragment::span(text::italic(" [*]")));
            }

            let title_widget = TextFragments::new(&fragments).alignment(Alignment::Center);
            frame.render_widget(title_widget, layout[0]);
        }

        // Items in panel

        macro_rules! draw_stat {
            ($x_column:expr, $y_column:expr => $header:expr, $value:expr) => {{
                let content = layout[1];

                let pos = content.grid_pos(Rect {
                    x: $x_column,
                    y: $y_column,
                    width: content.width / 3,
                    height: content.height / 3,
                });

                Self::draw_stat($header, $value, pos, frame);
            }};
        }

        // Left panel items

        draw_stat!(0, 0 => "Watch Time", {
            let watch_time_mins = info.episodes * info.episode_length_mins;
            util::hm_from_mins(f32::from(watch_time_mins))
        });

        draw_stat!(0, 1 => "Time Left", {
            let eps_left = info.episodes - entry.watched_episodes().min(info.episodes);
            let time_left_mins = eps_left * info.episode_length_mins;
            util::hm_from_mins(f32::from(time_left_mins))
        });

        draw_stat!(0, 2 => "Episode Length", format!("{}M", info.episode_length_mins));

        // Middle panel items

        draw_stat!(1, 0 => "Progress", format!("{}|{}", entry.watched_episodes(), info.episodes));

        draw_stat!(1, 1 => "Score", {
            match (entry.score(), &state.remote) {
                (Some(score), RemoteStatus::LoggedIn(remote)) => remote.score_to_str(score as u8),
                (Some(score), RemoteStatus::LoggingIn(_)) => score.to_string().into(),
                (None, _) => "??".into(),
            }
        });

        draw_stat!(1, 2 => "Status", {
            let status: &'static str = entry.status().into();
            status
        });

        // Right panel items

        // TODO: allow the format to be changed in the config
        let format_date = |date: Option<SeriesDate>| {
            date.map_or_else(
                || Cow::Borrowed("??"),
                |date| format!("{:02}/{:02}/{:02}", date.month, date.day, date.year % 100).into(),
            )
        };

        draw_stat!(2, 0 => "Start Date", format_date(entry.start_date()));
        draw_stat!(2, 1 => "Finish Date", format_date(entry.end_date()));
        draw_stat!(2, 2 => "Rewatched", entry.times_rewatched().to_string());

        self.draw_status_text(state, layout[2], frame);
    }

    fn draw_stat<B, S>(header: &str, value: S, rect: Rect, frame: &mut Frame<B>)
    where
        B: Backend,
        S: AsRef<str>,
    {
        let fragments = [
            Fragment::span(text::bold(header)),
            Fragment::Line,
            Fragment::span(text::italic(value.as_ref())),
        ];

        let widget = TextFragments::new(&fragments).alignment(Alignment::Center);
        frame.render_widget(widget, rect);
    }

    fn draw_status_text<B: Backend>(&self, state: &UIState, rect: Rect, frame: &mut Frame<B>) {
        let progress_remaining_secs = self.progress_remaining_secs.load(Ordering::SeqCst);

        // Remaining time until progression
        if progress_remaining_secs > 0 {
            let mins = (progress_remaining_secs as f32 / 60.0).round() as u32;

            let fragments = [
                Fragment::span(text::bold(mins.to_string())),
                Fragment::span(text::bold(" Minutes Until Progression")),
            ];

            let widget = TextFragments::new(&fragments).alignment(Alignment::Center);
            frame.render_widget(widget, rect);
        }
        // Login message
        else if let RemoteStatus::LoggingIn(username) = &state.remote {
            let fragments = [
                Fragment::span(text::bold("Logging In As ")),
                Fragment::Span(
                    text::bold_with(username, |s| s.fg(Color::Blue)),
                    SpanOptions::new().overflow(OverflowMode::Truncate),
                ),
            ];

            let widget = TextFragments::new(&fragments).alignment(Alignment::Center);
            frame.render_widget(widget, rect);
        }
    }

    pub fn draw<B: Backend>(&mut self, state: &UIState, rect: Rect, frame: &mut Frame<B>) {
        let info_block = block::with_borders("Info");
        frame.render_widget(info_block, rect);

        if state.users.get().is_empty() {
            return Self::draw_no_users_info(rect, frame);
        }

        match state.series.selected() {
            Some(LoadedSeries::Complete(series)) => {
                self.draw_series_info(state, series, rect, frame)
            }
            Some(LoadedSeries::Partial(_, err)) => Self::draw_series_error(err, rect, frame),
            Some(LoadedSeries::None(_, err)) => Self::draw_series_error(err, rect, frame),
            None => Self::draw_no_series_found(rect, frame),
        }
    }
}

impl Component for InfoPanel {
    type State = ();
    type KeyResult = ();

    fn process_key(&mut self, _: crate::key::Key, _: &mut Self::State) -> Self::KeyResult {}
}
<|endoftext|>


<commit_before>extern crate hmacsha1;
extern crate uuid;
extern crate byteorder;

use self::uuid::Uuid;

use std::time::{Instant,Duration};
use std::io::prelude::*;
use std::net::Shutdown;
use std::fs::File;
use std::net::TcpStream;
use std::collections::HashMap;

use std::thread;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel,Receiver,Sender};

use player::Player;
use chat::{read_text,write_text};
use opcode;
use events::Event;

pub const KEY_LEN: usize = 20;
pub const ID_LEN: usize = 16;

#[derive(Debug,Clone)]
pub struct ClientBase {
    pub key: Vec<u8>,
    pub id: Uuid,
}

#[derive(Debug)]
pub struct Client {
    pub base: ClientBase,
    pub stream: Option<TcpStream>,
    pub cache: HashMap<Uuid,Player>,
    pub msg: Vec<(Uuid,String)>, //cached messages of inbound chat
    pub ping_start: Instant,
    pub ping_delta: f32,

    pub events: Option<(Sender<Event>,Receiver<Event>)>,
}

impl Client {
    #[allow(dead_code)]
    pub fn new (key: [u8;KEY_LEN], uuid: Uuid) -> Client {
        Client { base: ClientBase { key:From::from(&key[..]),
                                    id:uuid, },
                 stream: None,
                 cache: HashMap::new(),
                 msg: vec!(),
                 ping_start: Instant::now(),
                 ping_delta: 0.0,

                 events: Some(channel()),
        }
    }

    #[allow(unused_must_use)]
    pub fn default () -> Client {
        let id = uuid::Uuid::new_v4();
        let m = hmacsha1::hmac_sha1(uuid::Uuid::new_v4().as_bytes(),
                                    id.as_bytes());

        Client::new(m,id)
    }
    #[allow(unused_must_use)]
    pub fn save (client: &Client, path: &str) -> bool {
        let f = File::create(path);
        if let Ok(mut f) = f {
            f.write_all(&client.base.key);
            f.write_all(client.base.id.as_bytes()).is_ok()
        }
        else { false }
    }

    pub fn id (&self) -> &Uuid {
        &self.base.id
    }

    pub fn key (&self) -> &[u8] {
        &self.base.key[..]
    }

    #[allow(dead_code)]
    pub fn key_as_ref (&self) -> &Vec<u8> {
        &self.base.key
    }

    pub fn load_file (path: &str) -> Option<Client> {
        let f = File::open(path);
        if let Ok(mut f) = f {
            return Client::load(&mut f)
        }

        None
    }

    pub fn load<S:Read> (s: &mut S) -> Option<Client> {
        let mut key = [0u8;KEY_LEN];
        let mut id = [0u8;ID_LEN];
        if let Ok(_) = s.read_exact(&mut key) {
            if let Ok(_) = s.read_exact(&mut id) {
                if let Ok(id) = Uuid::from_bytes(&id) {
                    return Some(Client::new(key, id))
                }
                else { println!("cannot uuid file") }
            }
        }

        None
    }

    #[allow(unused_must_use)]
    pub fn connect (&mut self, server: &str)  {
        if let Ok(s) = TcpStream::connect(server) {
            self.stream = Some(s);
        }
    }

    #[allow(unused_must_use)]
    pub fn login (client: &Arc<Mutex<Client>>) -> bool {
        let c = client.clone();
        if let Ok(ref mut client) = client.lock() {
            let base = client.base.clone();

            if let Some(ref mut s) = client.stream {

                let mut m = [0u8;ID_LEN];
                if let Ok(_) = s.read_exact(&mut m) {
                    s.write_all(&[0]);

                    let hm = hmacsha1::hmac_sha1(&base.key, &m);

                    s.write_all(&hm);
                    s.write_all(base.id.as_bytes());

                    thread::spawn(move || {
                        Client::handler(c)
                    });

                    return true
                }
            }
        }

        false
    }

    #[allow(unused_must_use)]
    pub fn register (&mut self) {
        if let Some(ref mut s) = self.stream {
            s.write_all(&[1]);
            s.write_all(&self.base.key);
            s.write_all(self.base.id.as_bytes());
        }
    }

    #[allow(unused_must_use)]
    pub fn chat (&mut self, text: &str) {
        if let Some(ref mut s) = self.stream {
            write_text(s, text);
        }
    }

    #[allow(unused_must_use)]
    pub fn nick (&mut self, nick: &str) {
        if let Some(ref mut s) = self.stream {
            s.write_all(&Player::to_bytes(None,
                                          &Player { nick: nick.to_owned() }));
        }
    }

    #[allow(unused_must_use)]
    pub fn ping (&mut self) -> bool {
        if let Some(ref mut s) = self.stream {
            self.ping_start = Instant::now();

            return s.write(&[opcode::PING]).is_ok()
        }

        false
    }

    fn handler (client: Arc<Mutex<Client>>) {
        let mut cmd = [0u8;1];
        let mut s; // NOTE: this should be read from only!

        if let Ok(mut client) = client.lock() {
            client.ping();

            if let Some(ref s_) = client.stream {
                s = s_.try_clone().unwrap();
            }
            else { return }
        }
        else { return }

        'handler: loop {
            if let Ok(_) = s.read_exact(&mut cmd) {
                match cmd[0] {
                    opcode::CHAT => {
                        if let Some(text) = read_text(&mut s) {
                            let mut id = [0u8;ID_LEN];
                            if let Ok(_) = s.read_exact(&mut id) {
                                if let Ok(uuid) = Uuid::from_bytes(&id) {
                                    if let Ok(mut client) = client.lock() {
                                        client.msg.push((uuid,text));

                                        if let Some(ref events) = client.events {
                                            let _ = events.0.send(Event(opcode::CHAT,
                                                                        uuid));
                                        }
                                    }
                                }
                            }
                        }
                    },
                    opcode::PLAYER => {
                        if let (Some(uuid),Some(player)) = Player::from_stream(&mut s, true) {
                            if let Ok(mut client) = client.lock() {
                                client.cache.insert(uuid, player);

                                if let Some(ref events) = client.events {
                                    let _ = events.0.send(Event(opcode::PLAYER,
                                                                uuid));
                                }
                            }
                        }
                    },
                    opcode::PING => { // recv server ping, respond
                        if let Ok(mut client) = client.lock() {
                            if let Some(ref mut s) = client.stream {
                                let _ = s.write_all(&[opcode::PONG]);
                            }
                        }
                    },
                    opcode::PONG => { // recv client ping back?
                        if let Ok(mut client) = client.lock() {
                            client.ping_delta = client.ping_start.elapsed().as_secs() as f32;
                        }
                    },
                    _ => { },
                }
            }

            // check ping delay, shutdown
            if let Ok(mut client) = client.lock() {
                client.ping_delta += client.ping_start.elapsed().as_secs() as f32;
                if client.ping_delta > 8.0 { break 'handler }

                // ping every so often
                if client.ping_start.elapsed() > Duration::new(5, 0) {
                    if !client.ping() { break 'handler }
                }
            }
        }

        if let Ok(mut client) = client.lock() {
            client.shutdown();
        }
    }

    pub fn shutdown(&mut self) {
        if let Some(ref mut s) = self.stream {
            let _ = s.flush();
            let _ = s.shutdown(Shutdown::Both);
        }

        self.stream = None;
    }
}

impl Drop for Client {
    fn drop (&mut self) {
        self.shutdown();
    }
}
<commit_msg>make explicit<commit_after>extern crate hmacsha1;
extern crate uuid;
extern crate byteorder;

use self::uuid::Uuid;

use std::time::{Instant,Duration};
use std::io::prelude::*;
use std::net::Shutdown;
use std::fs::File;
use std::net::TcpStream;
use std::collections::HashMap;

use std::thread;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel,Receiver,Sender};

use player::Player;
use chat::{read_text,write_text};
use opcode;
use events::Event;

pub const KEY_LEN: usize = 20;
pub const ID_LEN: usize = 16;

#[derive(Debug,Clone)]
pub struct ClientBase {
    pub key: Vec<u8>,
    pub id: Uuid,
}

#[derive(Debug)]
pub struct Client {
    pub base: ClientBase,
    pub stream: Option<TcpStream>,
    pub cache: HashMap<Uuid,Player>,
    pub msg: Vec<(Uuid,String)>, //cached messages of inbound chat
    pub ping_start: Instant,
    pub ping_delta: f32,

    pub events: (Sender<Event>,Receiver<Event>),
}

impl Client {
    #[allow(dead_code)]
    pub fn new (key: [u8;KEY_LEN], uuid: Uuid) -> Client {
        Client { base: ClientBase { key:From::from(&key[..]),
                                    id:uuid, },
                 stream: None,
                 cache: HashMap::new(),
                 msg: vec!(),
                 ping_start: Instant::now(),
                 ping_delta: 0.0,

                 events: channel(),
        }
    }

    #[allow(unused_must_use)]
    pub fn default () -> Client {
        let id = uuid::Uuid::new_v4();
        let m = hmacsha1::hmac_sha1(uuid::Uuid::new_v4().as_bytes(),
                                    id.as_bytes());

        Client::new(m,id)
    }
    #[allow(unused_must_use)]
    pub fn save (client: &Client, path: &str) -> bool {
        let f = File::create(path);
        if let Ok(mut f) = f {
            f.write_all(&client.base.key);
            f.write_all(client.base.id.as_bytes()).is_ok()
        }
        else { false }
    }

    pub fn id (&self) -> &Uuid {
        &self.base.id
    }

    pub fn key (&self) -> &[u8] {
        &self.base.key[..]
    }

    #[allow(dead_code)]
    pub fn key_as_ref (&self) -> &Vec<u8> {
        &self.base.key
    }

    pub fn load_file (path: &str) -> Option<Client> {
        let f = File::open(path);
        if let Ok(mut f) = f {
            return Client::load(&mut f)
        }

        None
    }

    pub fn load<S:Read> (s: &mut S) -> Option<Client> {
        let mut key = [0u8;KEY_LEN];
        let mut id = [0u8;ID_LEN];
        if let Ok(_) = s.read_exact(&mut key) {
            if let Ok(_) = s.read_exact(&mut id) {
                if let Ok(id) = Uuid::from_bytes(&id) {
                    return Some(Client::new(key, id))
                }
                else { println!("cannot uuid file") }
            }
        }

        None
    }

    #[allow(unused_must_use)]
    pub fn connect (&mut self, server: &str)  {
        if let Ok(s) = TcpStream::connect(server) {
            self.stream = Some(s);
        }
    }

    #[allow(unused_must_use)]
    pub fn login (client: &Arc<Mutex<Client>>) -> bool {
        let c = client.clone();
        if let Ok(ref mut client) = client.lock() {
            let base = client.base.clone();

            if let Some(ref mut s) = client.stream {

                let mut m = [0u8;ID_LEN];
                if let Ok(_) = s.read_exact(&mut m) {
                    s.write_all(&[0]);

                    let hm = hmacsha1::hmac_sha1(&base.key, &m);

                    s.write_all(&hm);
                    s.write_all(base.id.as_bytes());

                    thread::spawn(move || {
                        Client::handler(c)
                    });

                    return true
                }
            }
        }

        false
    }

    #[allow(unused_must_use)]
    pub fn register (&mut self) {
        if let Some(ref mut s) = self.stream {
            s.write_all(&[1]);
            s.write_all(&self.base.key);
            s.write_all(self.base.id.as_bytes());
        }
    }

    #[allow(unused_must_use)]
    pub fn chat (&mut self, text: &str) {
        if let Some(ref mut s) = self.stream {
            write_text(s, text);
        }
    }

    #[allow(unused_must_use)]
    pub fn nick (&mut self, nick: &str) {
        if let Some(ref mut s) = self.stream {
            s.write_all(&Player::to_bytes(None,
                                          &Player { nick: nick.to_owned() }));
        }
    }

    #[allow(unused_must_use)]
    pub fn ping (&mut self) -> bool {
        if let Some(ref mut s) = self.stream {
            self.ping_start = Instant::now();

            return s.write(&[opcode::PING]).is_ok()
        }

        false
    }

    fn handler (client: Arc<Mutex<Client>>) {
        let mut cmd = [0u8;1];
        let mut s; // NOTE: this should be read from only!

        if let Ok(mut client) = client.lock() {
            client.ping();

            if let Some(ref s_) = client.stream {
                s = s_.try_clone().unwrap();
            }
            else { return }
        }
        else { return }

        'handler: loop {
            if let Ok(_) = s.read_exact(&mut cmd) {
                match cmd[0] {
                    opcode::CHAT => {
                        if let Some(text) = read_text(&mut s) {
                            let mut id = [0u8;ID_LEN];
                            if let Ok(_) = s.read_exact(&mut id) {
                                if let Ok(uuid) = Uuid::from_bytes(&id) {
                                    if let Ok(mut client) = client.lock() {
                                        client.msg.push((uuid,text));

                                        let _ = client.events.0.send(Event(opcode::CHAT,
                                                                           uuid));
                                    }
                                }
                            }
                        }
                    },
                    opcode::PLAYER => {
                        if let (Some(uuid),Some(player)) = Player::from_stream(&mut s, true) {
                            if let Ok(mut client) = client.lock() {
                                client.cache.insert(uuid, player);

                                let _ = client.events.0.send(Event(opcode::PLAYER,
                                                                   uuid));
                            }
                        }
                    },
                    opcode::PING => { // recv server ping, respond
                        if let Ok(mut client) = client.lock() {
                            if let Some(ref mut s) = client.stream {
                                let _ = s.write_all(&[opcode::PONG]);
                            }
                        }
                    },
                    opcode::PONG => { // recv client ping back?
                        if let Ok(mut client) = client.lock() {
                            client.ping_delta = client.ping_start.elapsed().as_secs() as f32;
                        }
                    },
                    _ => { },
                }
            }

            // check ping delay, shutdown
            if let Ok(mut client) = client.lock() {
                client.ping_delta += client.ping_start.elapsed().as_secs() as f32;
                if client.ping_delta > 8.0 { break 'handler }

                // ping every so often
                if client.ping_start.elapsed() > Duration::new(5, 0) {
                    if !client.ping() { break 'handler }
                }
            }
        }

        if let Ok(mut client) = client.lock() {
            client.shutdown();
        }
    }

    pub fn shutdown(&mut self) {
        if let Some(ref mut s) = self.stream {
            let _ = s.flush();
            let _ = s.shutdown(Shutdown::Both);
        }

        self.stream = None;
    }
}

impl Drop for Client {
    fn drop (&mut self) {
        self.shutdown();
    }
}
<|endoftext|>


<commit_before>use std::cmp::Reverse;
use std::collections::BinaryHeap;
use std::collections::HashSet;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Read;

use common::manhattan_distance;
use common::Solution;

type Coordinate = (usize, usize);

#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Copy, Clone, Debug)]
struct State {
    pos: Coordinate,
    climbing: bool,
    torch: bool,
}

impl State {
    pub fn is_valid(&self, terrain: usize) -> bool {
        match terrain {
            0 => self.torch || self.climbing,
            1 => !self.torch,
            2 => !self.climbing,
            _ => panic!(),
        }
    }
}

const MOD_BASE: usize = 20183;

#[derive(Default)]
pub struct Day22 {}

impl Day22 {
    pub fn new() -> Self {
        Default::default()
    }
}

fn compute_table((x, y): Coordinate, depth: usize) -> Vec<Vec<usize>> {
    let mut table = vec![vec![0usize; x + 1]; y + 1];
    table[0][0] = 0;
    for x in 1..=x {
        table[0][x] = (16807 * x + depth) % MOD_BASE;
    }

    for y in 1..=y {
        table[y][0] = (48271 * y + depth) % MOD_BASE;
    }

    for y in 1..=y {
        for x in 1..=x {
            table[y][x] = (table[y - 1][x] * table[y][x - 1] + depth) % MOD_BASE;
        }
    }

    for c in table.iter_mut().flat_map(|x| x.iter_mut()) {
        *c %= 3;
    }

    table
}

fn read_input(input: &mut Read) -> (usize, Coordinate) {
    let mut buf = String::new();
    let mut reader = BufReader::new(input);
    reader.read_line(&mut buf).unwrap();

    let depth: usize;
    {
        let mut parts = buf.trim().split(' ');
        depth = parts.nth(1).unwrap().parse().unwrap();
    }
    buf.clear();
    reader.read_line(&mut buf).unwrap();

    let target: Coordinate;
    {
        let mut parts = buf.trim().split(|c| c == ',' || c == ' ');
        let x: usize = parts.nth(1).unwrap().parse().unwrap();
        let y: usize = parts.next().unwrap().parse().unwrap();
        target = (x, y);
    }

    (depth, target)
}

impl Solution for Day22 {
    fn part1(&mut self, input: &mut Read) -> String {
        let (depth, target) = read_input(input);
        let mut table = compute_table(target, depth);
        table[target.1][target.0] = 0;

        let result: usize = table.iter().flat_map(|x| x.iter())
            .sum();
        format!("{}", result)
    }

    fn part2(&mut self, input: &mut Read) -> String {
        let (depth, target) = read_input(input);
        let mut table = compute_table((target.0 + 100, target.1 + 100), depth);
        table[target.1][target.0] = 0;

        let mut todo = BinaryHeap::new();
        let mut visited: HashSet<State> = HashSet::new();
        let target_state = State { pos: target, climbing: false, torch: true };

        todo.push((Reverse(manhattan_distance((0, 0), target)), Reverse(0), State { pos: (0, 0), climbing: false, torch: true }));

        while let Some((Reverse(approx), Reverse(dist), state)) = todo.pop() {
            if visited.contains(&state) {
                continue;
            }
            visited.insert(state);
            if state == target_state {
                return format!("{}", dist);
            }

            let (x, y) = state.pos;

            // Handle equipment changes
            let changes = [
                State { pos: state.pos, climbing: state.climbing, torch: !state.torch },
                State { pos: state.pos, climbing: !state.climbing, torch: state.torch },
                State { pos: state.pos, climbing: !state.climbing, torch: !state.torch },
            ];

            for state in changes.iter().cloned() {
                if visited.contains(&state) || !state.is_valid(table[y][x]) {
                    continue;
                }

                todo.push((Reverse(approx + 7), Reverse(dist + 7), state));
            }

            let xmin = if x == 0 { 0 } else { x - 1 };
            let ymin = if y == 0 { 0 } else { y - 1 };

            for xn in xmin..=(x + 1).min(target.0 + 100) {
                for yn in ymin..=(y + 1).min(target.1 + 100) {
                    let new_state = State {
                        pos: (xn, yn),
                        torch: state.torch,
                        climbing: state.climbing,
                    };

                    if !visited.contains(&new_state) && new_state.is_valid(table[yn][xn]) && (x == xn || y == yn) {
                        todo.push((Reverse(dist + 1 + manhattan_distance(target, new_state.pos)), Reverse(dist + 1), new_state));
                    }
                }
            }
        }
        unreachable!();
    }
}

#[cfg(test)]
mod tests {
    use common::Solution;

    use super::*;

    const SAMPLE_INPUT: &[u8] = include_bytes!("samples/22.txt");

    #[test]
    fn sample_part1() {
        let mut instance = Day22::new();
        assert_eq!("114", instance.part1(&mut SAMPLE_INPUT));
    }

    #[test]
    fn sample_part2() {
        let mut instance = Day22::new();
        assert_eq!("45", instance.part2(&mut SAMPLE_INPUT));
    }
}
<commit_msg>Reasonable bounds.<commit_after>use std::cmp::Reverse;
use std::collections::BinaryHeap;
use std::collections::HashSet;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Read;

use common::manhattan_distance;
use common::Solution;

type Coordinate = (usize, usize);

#[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Copy, Clone, Debug)]
struct State {
    pos: Coordinate,
    climbing: bool,
    torch: bool,
}

impl State {
    pub fn is_valid(&self, terrain: usize) -> bool {
        match terrain {
            0 => self.torch || self.climbing,
            1 => !self.torch,
            2 => !self.climbing,
            _ => panic!(),
        }
    }
}

const MOD_BASE: usize = 20183;

#[derive(Default)]
pub struct Day22 {}

impl Day22 {
    pub fn new() -> Self {
        Default::default()
    }
}

fn compute_table((x, y): Coordinate, depth: usize) -> Vec<Vec<usize>> {
    let mut table = vec![vec![0usize; x + 1]; y + 1];
    table[0][0] = 0;
    for x in 1..=x {
        table[0][x] = (16807 * x + depth) % MOD_BASE;
    }

    for y in 1..=y {
        table[y][0] = (48271 * y + depth) % MOD_BASE;
    }

    for y in 1..=y {
        for x in 1..=x {
            table[y][x] = (table[y - 1][x] * table[y][x - 1] + depth) % MOD_BASE;
        }
    }

    for c in table.iter_mut().flat_map(|x| x.iter_mut()) {
        *c %= 3;
    }

    table
}

fn read_input(input: &mut Read) -> (usize, Coordinate) {
    let mut buf = String::new();
    let mut reader = BufReader::new(input);
    reader.read_line(&mut buf).unwrap();

    let depth: usize;
    {
        let mut parts = buf.trim().split(' ');
        depth = parts.nth(1).unwrap().parse().unwrap();
    }
    buf.clear();
    reader.read_line(&mut buf).unwrap();

    let target: Coordinate;
    {
        let mut parts = buf.trim().split(|c| c == ',' || c == ' ');
        let x: usize = parts.nth(1).unwrap().parse().unwrap();
        let y: usize = parts.next().unwrap().parse().unwrap();
        target = (x, y);
    }

    (depth, target)
}

impl Solution for Day22 {
    fn part1(&mut self, input: &mut Read) -> String {
        let (depth, target) = read_input(input);
        let mut table = compute_table(target, depth);
        table[target.1][target.0] = 0;

        let result: usize = table.iter().flat_map(|x| x.iter())
            .sum();
        format!("{}", result)
    }

    fn part2(&mut self, input: &mut Read) -> String {
        let (depth, target) = read_input(input);
        let mut table = compute_table((target.0 + 200, target.1 + 200), depth);
        table[target.1][target.0] = 0;

        let mut todo = BinaryHeap::new();
        let mut visited: HashSet<State> = HashSet::new();
        let target_state = State { pos: target, climbing: false, torch: true };

        todo.push((Reverse(manhattan_distance((0, 0), target)), Reverse(0), State { pos: (0, 0), climbing: false, torch: true }));

        while let Some((Reverse(approx), Reverse(dist), state)) = todo.pop() {
            if visited.contains(&state) {
                continue;
            }
            visited.insert(state);
            if state == target_state {
                return format!("{}", dist);
            }

            let (x, y) = state.pos;

            // Handle equipment changes
            let changes = [
                State { pos: state.pos, climbing: state.climbing, torch: !state.torch },
                State { pos: state.pos, climbing: !state.climbing, torch: state.torch },
                State { pos: state.pos, climbing: !state.climbing, torch: !state.torch },
            ];

            for state in changes.iter().cloned() {
                if visited.contains(&state) || !state.is_valid(table[y][x]) {
                    continue;
                }

                todo.push((Reverse(approx + 7), Reverse(dist + 7), state));
            }

            let xmin = if x == 0 { 0 } else { x - 1 };
            let ymin = if y == 0 { 0 } else { y - 1 };

            for xn in xmin..=(x + 1) {
                for yn in ymin..=(y + 1) {
                    let new_state = State {
                        pos: (xn, yn),
                        torch: state.torch,
                        climbing: state.climbing,
                    };

                    if !visited.contains(&new_state) && new_state.is_valid(table[yn][xn]) && (x == xn || y == yn) {
                        todo.push((Reverse(dist + 1 + manhattan_distance(target, new_state.pos)), Reverse(dist + 1), new_state));
                    }
                }
            }
        }
        unreachable!();
    }
}

#[cfg(test)]
mod tests {
    use common::Solution;

    use super::*;

    const SAMPLE_INPUT: &[u8] = include_bytes!("samples/22.txt");

    #[test]
    fn sample_part1() {
        let mut instance = Day22::new();
        assert_eq!("114", instance.part1(&mut SAMPLE_INPUT));
    }

    #[test]
    fn sample_part2() {
        let mut instance = Day22::new();
        assert_eq!("45", instance.part2(&mut SAMPLE_INPUT));
    }
}
<|endoftext|>


<commit_before>use core::iterator::{ IteratorUtil };

use common::extvec;
use common::prime::{ Prime, factors_to_uint };
use common::monoid::{ mergei_as, Max };
use common::problem::{ Problem };

pub static problem: Problem<'static> = Problem {
    id: 5,
    answer: "232792560",
    solver: solve
};

fn solve() -> ~str {
    let mut ps = Prime::new();
    let mut fs = ~[];

    for uint::range(1, 20 + 1) |n| {
        fs.push(extvec::from_iter(ps.factorize(n)));
    };

    let mut v = mergei_as(fs, Max);
    return factors_to_uint(v.iter().transform(|&x| x)).to_str();
}
<commit_msg>Use MergeMultMonoidIterator instead of mergei_as<commit_after>use core::iterator::{ IteratorUtil };

use common::prime::{ Prime, factors_to_uint };
use common::monoid::{ Max, MergeMultiMonoidIterator, Unwrap };
use common::problem::{ Problem };

pub static problem: Problem<'static> = Problem {
    id: 5,
    answer: "232792560",
    solver: solve
};

fn solve() -> ~str {
    let mut ps = Prime::new();

    let fs = do vec::from_fn(20) |i| { ps.factorize(i + 1) };
    let it = MergeMultiMonoidIterator::new(
        fs.map(|&x| x.transform(|(base, exp)| (base, Max(exp))))
    ).transform(|(base, m)| (base, m.unwrap()));
    return factors_to_uint(it).to_str();
}
<|endoftext|>


<commit_before>//! Defines database & queries for macro expansion.

use std::sync::Arc;

use mbe::MacroRules;
use ra_db::{salsa, SourceDatabase};
use ra_parser::FragmentKind;
use ra_prof::profile;
use ra_syntax::{AstNode, Parse, SyntaxKind::*, SyntaxNode};

use crate::{
    ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId,
    HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind,
    MacroFile,
};

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum TokenExpander {
    MacroRules(mbe::MacroRules),
    Builtin(BuiltinFnLikeExpander),
    BuiltinDerive(BuiltinDeriveExpander),
}

impl TokenExpander {
    pub fn expand(
        &self,
        db: &dyn AstDatabase,
        id: LazyMacroId,
        tt: &tt::Subtree,
    ) -> Result<tt::Subtree, mbe::ExpandError> {
        match self {
            TokenExpander::MacroRules(it) => it.expand(tt),
            TokenExpander::Builtin(it) => it.expand(db, id, tt),
            TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt),
        }
    }

    pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId {
        match self {
            TokenExpander::MacroRules(it) => it.map_id_down(id),
            TokenExpander::Builtin(..) => id,
            TokenExpander::BuiltinDerive(..) => id,
        }
    }

    pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) {
        match self {
            TokenExpander::MacroRules(it) => it.map_id_up(id),
            TokenExpander::Builtin(..) => (id, mbe::Origin::Call),
            TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call),
        }
    }
}

// FIXME: rename to ExpandDatabase
#[salsa::query_group(AstDatabaseStorage)]
pub trait AstDatabase: SourceDatabase {
    fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;

    #[salsa::transparent]
    fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>;

    #[salsa::interned]
    fn intern_macro(&self, macro_call: MacroCallLoc) -> LazyMacroId;
    fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>;
    fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>;
    fn parse_macro(&self, macro_file: MacroFile)
        -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>;
    fn macro_expand(&self, macro_call: MacroCallId) -> Result<Arc<tt::Subtree>, String>;

    #[salsa::interned]
    fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId;
}

pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
    let map =
        db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it));
    Arc::new(map)
}

pub(crate) fn macro_def(
    db: &dyn AstDatabase,
    id: MacroDefId,
) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
    match id.kind {
        MacroDefKind::Declarative => {
            let macro_call = id.ast_id?.to_node(db);
            let arg = macro_call.token_tree()?;
            let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
                log::warn!("fail on macro_def to token tree: {:#?}", arg);
                None
            })?;
            let rules = match MacroRules::parse(&tt) {
                Ok(it) => it,
                Err(err) => {
                    log::warn!("fail on macro_def parse: error: {:#?} {:#?}", err, tt);
                    return None;
                }
            };
            Some(Arc::new((TokenExpander::MacroRules(rules), tmap)))
        }
        MacroDefKind::BuiltIn(expander) => {
            Some(Arc::new((TokenExpander::Builtin(expander), mbe::TokenMap::default())))
        }
        MacroDefKind::BuiltInDerive(expander) => {
            Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default())))
        }
        MacroDefKind::BuiltInEager(_expander) => None,
    }
}

pub(crate) fn macro_arg(
    db: &dyn AstDatabase,
    id: MacroCallId,
) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
    let id = match id {
        MacroCallId::LazyMacro(id) => id,
        MacroCallId::EagerMacro(_id) => {
            // FIXME: support macro_arg for eager macro
            return None;
        }
    };
    let loc = db.lookup_intern_macro(id);
    let arg = loc.kind.arg(db)?;
    let (tt, tmap) = mbe::syntax_node_to_token_tree(&arg)?;
    Some(Arc::new((tt, tmap)))
}

pub(crate) fn macro_expand(
    db: &dyn AstDatabase,
    id: MacroCallId,
) -> Result<Arc<tt::Subtree>, String> {
    let lazy_id = match id {
        MacroCallId::LazyMacro(id) => id,
        MacroCallId::EagerMacro(id) => {
            return Ok(db.lookup_intern_eager_expansion(id).subtree);
        }
    };

    let loc = db.lookup_intern_macro(lazy_id);
    let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?;

    let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
    let tt = macro_rules.0.expand(db, lazy_id, &macro_arg.0).map_err(|err| format!("{:?}", err))?;
    // Set a hard limit for the expanded tt
    let count = tt.count();
    if count > 65536 {
        return Err(format!("Total tokens count exceed limit : count = {}", count));
    }
    Ok(Arc::new(tt))
}

pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> {
    match file_id.0 {
        HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
        HirFileIdRepr::MacroFile(macro_file) => {
            db.parse_macro(macro_file).map(|(it, _)| it.syntax_node())
        }
    }
}

pub(crate) fn parse_macro(
    db: &dyn AstDatabase,
    macro_file: MacroFile,
) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> {
    let _p = profile("parse_macro_query");

    let macro_call_id = macro_file.macro_call_id;
    let tt = db
        .macro_expand(macro_call_id)
        .map_err(|err| {
            // Note:
            // The final goal we would like to make all parse_macro success,
            // such that the following log will not call anyway.
            let loc: MacroCallLoc = db.lookup_intern_macro(macro_call_id);
            let node = loc.kind.node(db);
            log::warn!("fail on macro_parse: (reason: {} macro_call: {:#})", err, node.value);
        })
        .ok()?;

    let fragment_kind = to_fragment_kind(db, macro_call_id);

    let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?;
    Some((parse, Arc::new(rev_token_map)))
}

/// Given a `MacroCallId`, return what `FragmentKind` it belongs to.
/// FIXME: Not completed
fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
    let lazy_id = match id {
        MacroCallId::LazyMacro(id) => id,
        MacroCallId::EagerMacro(id) => {
            return db.lookup_intern_eager_expansion(id).fragment;
        }
    };
    let syn = db.lookup_intern_macro(lazy_id).kind.node(db).value;

    let parent = match syn.parent() {
        Some(it) => it,
        None => {
            // FIXME:
            // If it is root, which means the parent HirFile
            // MacroKindFile must be non-items
            // return expr now.
            return FragmentKind::Expr;
        }
    };

    match parent.kind() {
        MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items,
        ITEM_LIST => FragmentKind::Items,
        LET_STMT => {
            // FIXME: Handle Pattern
            FragmentKind::Expr
        }
        // FIXME: Expand to statements in appropriate positions; HIR lowering needs to handle that
        EXPR_STMT | BLOCK => FragmentKind::Expr,
        ARG_LIST => FragmentKind::Expr,
        TRY_EXPR => FragmentKind::Expr,
        TUPLE_EXPR => FragmentKind::Expr,
        PAREN_EXPR => FragmentKind::Expr,

        FOR_EXPR => FragmentKind::Expr,
        PATH_EXPR => FragmentKind::Expr,
        LAMBDA_EXPR => FragmentKind::Expr,
        CONDITION => FragmentKind::Expr,
        BREAK_EXPR => FragmentKind::Expr,
        RETURN_EXPR => FragmentKind::Expr,
        BLOCK_EXPR => FragmentKind::Expr,
        MATCH_EXPR => FragmentKind::Expr,
        MATCH_ARM => FragmentKind::Expr,
        MATCH_GUARD => FragmentKind::Expr,
        RECORD_FIELD => FragmentKind::Expr,
        CALL_EXPR => FragmentKind::Expr,
        INDEX_EXPR => FragmentKind::Expr,
        METHOD_CALL_EXPR => FragmentKind::Expr,
        AWAIT_EXPR => FragmentKind::Expr,
        CAST_EXPR => FragmentKind::Expr,
        REF_EXPR => FragmentKind::Expr,
        PREFIX_EXPR => FragmentKind::Expr,
        RANGE_EXPR => FragmentKind::Expr,
        BIN_EXPR => FragmentKind::Expr,
        _ => {
            // Unknown , Just guess it is `Items`
            FragmentKind::Items
        }
    }
}
<commit_msg>Fix  for rebasing<commit_after>//! Defines database & queries for macro expansion.

use std::sync::Arc;

use mbe::MacroRules;
use ra_db::{salsa, SourceDatabase};
use ra_parser::FragmentKind;
use ra_prof::profile;
use ra_syntax::{AstNode, Parse, SyntaxKind::*, SyntaxNode};

use crate::{
    ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId,
    HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind,
    MacroFile,
};

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum TokenExpander {
    MacroRules(mbe::MacroRules),
    Builtin(BuiltinFnLikeExpander),
    BuiltinDerive(BuiltinDeriveExpander),
}

impl TokenExpander {
    pub fn expand(
        &self,
        db: &dyn AstDatabase,
        id: LazyMacroId,
        tt: &tt::Subtree,
    ) -> Result<tt::Subtree, mbe::ExpandError> {
        match self {
            TokenExpander::MacroRules(it) => it.expand(tt),
            TokenExpander::Builtin(it) => it.expand(db, id, tt),
            TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt),
        }
    }

    pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId {
        match self {
            TokenExpander::MacroRules(it) => it.map_id_down(id),
            TokenExpander::Builtin(..) => id,
            TokenExpander::BuiltinDerive(..) => id,
        }
    }

    pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) {
        match self {
            TokenExpander::MacroRules(it) => it.map_id_up(id),
            TokenExpander::Builtin(..) => (id, mbe::Origin::Call),
            TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call),
        }
    }
}

// FIXME: rename to ExpandDatabase
#[salsa::query_group(AstDatabaseStorage)]
pub trait AstDatabase: SourceDatabase {
    fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;

    #[salsa::transparent]
    fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>;

    #[salsa::interned]
    fn intern_macro(&self, macro_call: MacroCallLoc) -> LazyMacroId;
    fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>;
    fn macro_def(&self, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>>;
    fn parse_macro(&self, macro_file: MacroFile)
        -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>;
    fn macro_expand(&self, macro_call: MacroCallId) -> Result<Arc<tt::Subtree>, String>;

    #[salsa::interned]
    fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId;
}

pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
    let map =
        db.parse_or_expand(file_id).map_or_else(AstIdMap::default, |it| AstIdMap::from_source(&it));
    Arc::new(map)
}

pub(crate) fn macro_def(
    db: &dyn AstDatabase,
    id: MacroDefId,
) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
    match id.kind {
        MacroDefKind::Declarative => {
            let macro_call = id.ast_id?.to_node(db);
            let arg = macro_call.token_tree()?;
            let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
                log::warn!("fail on macro_def to token tree: {:#?}", arg);
                None
            })?;
            let rules = match MacroRules::parse(&tt) {
                Ok(it) => it,
                Err(err) => {
                    log::warn!("fail on macro_def parse: error: {:#?} {:#?}", err, tt);
                    return None;
                }
            };
            Some(Arc::new((TokenExpander::MacroRules(rules), tmap)))
        }
        MacroDefKind::BuiltIn(expander) => {
            Some(Arc::new((TokenExpander::Builtin(expander), mbe::TokenMap::default())))
        }
        MacroDefKind::BuiltInDerive(expander) => {
            Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default())))
        }
        MacroDefKind::BuiltInEager(_expander) => None,
    }
}

pub(crate) fn macro_arg(
    db: &dyn AstDatabase,
    id: MacroCallId,
) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
    let id = match id {
        MacroCallId::LazyMacro(id) => id,
        MacroCallId::EagerMacro(_id) => {
            // FIXME: support macro_arg for eager macro
            return None;
        }
    };
    let loc = db.lookup_intern_macro(id);
    let arg = loc.kind.arg(db)?;
    let (tt, tmap) = mbe::syntax_node_to_token_tree(&arg)?;
    Some(Arc::new((tt, tmap)))
}

pub(crate) fn macro_expand(
    db: &dyn AstDatabase,
    id: MacroCallId,
) -> Result<Arc<tt::Subtree>, String> {
    let lazy_id = match id {
        MacroCallId::LazyMacro(id) => id,
        MacroCallId::EagerMacro(id) => {
            return Ok(db.lookup_intern_eager_expansion(id).subtree);
        }
    };

    let loc = db.lookup_intern_macro(lazy_id);
    let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?;

    let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
    let tt = macro_rules.0.expand(db, lazy_id, &macro_arg.0).map_err(|err| format!("{:?}", err))?;
    // Set a hard limit for the expanded tt
    let count = tt.count();
    if count > 65536 {
        return Err(format!("Total tokens count exceed limit : count = {}", count));
    }
    Ok(Arc::new(tt))
}

pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> {
    match file_id.0 {
        HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
        HirFileIdRepr::MacroFile(macro_file) => {
            db.parse_macro(macro_file).map(|(it, _)| it.syntax_node())
        }
    }
}

pub(crate) fn parse_macro(
    db: &dyn AstDatabase,
    macro_file: MacroFile,
) -> Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> {
    let _p = profile("parse_macro_query");

    let macro_call_id = macro_file.macro_call_id;
    let tt = db
        .macro_expand(macro_call_id)
        .map_err(|err| {
            // Note:
            // The final goal we would like to make all parse_macro success,
            // such that the following log will not call anyway.
            match macro_call_id {
                MacroCallId::LazyMacro(id) => {
                    let loc: MacroCallLoc = db.lookup_intern_macro(id);
                    let node = loc.kind.node(db);
                    log::warn!(
                        "fail on macro_parse: (reason: {} macro_call: {:#})",
                        err,
                        node.value
                    );
                }
                _ => {
                    log::warn!("fail on macro_parse: (reason: {})", err);
                }
            }
        })
        .ok()?;

    let fragment_kind = to_fragment_kind(db, macro_call_id);

    let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?;
    Some((parse, Arc::new(rev_token_map)))
}

/// Given a `MacroCallId`, return what `FragmentKind` it belongs to.
/// FIXME: Not completed
fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
    let lazy_id = match id {
        MacroCallId::LazyMacro(id) => id,
        MacroCallId::EagerMacro(id) => {
            return db.lookup_intern_eager_expansion(id).fragment;
        }
    };
    let syn = db.lookup_intern_macro(lazy_id).kind.node(db).value;

    let parent = match syn.parent() {
        Some(it) => it,
        None => {
            // FIXME:
            // If it is root, which means the parent HirFile
            // MacroKindFile must be non-items
            // return expr now.
            return FragmentKind::Expr;
        }
    };

    match parent.kind() {
        MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items,
        ITEM_LIST => FragmentKind::Items,
        LET_STMT => {
            // FIXME: Handle Pattern
            FragmentKind::Expr
        }
        // FIXME: Expand to statements in appropriate positions; HIR lowering needs to handle that
        EXPR_STMT | BLOCK => FragmentKind::Expr,
        ARG_LIST => FragmentKind::Expr,
        TRY_EXPR => FragmentKind::Expr,
        TUPLE_EXPR => FragmentKind::Expr,
        PAREN_EXPR => FragmentKind::Expr,

        FOR_EXPR => FragmentKind::Expr,
        PATH_EXPR => FragmentKind::Expr,
        LAMBDA_EXPR => FragmentKind::Expr,
        CONDITION => FragmentKind::Expr,
        BREAK_EXPR => FragmentKind::Expr,
        RETURN_EXPR => FragmentKind::Expr,
        BLOCK_EXPR => FragmentKind::Expr,
        MATCH_EXPR => FragmentKind::Expr,
        MATCH_ARM => FragmentKind::Expr,
        MATCH_GUARD => FragmentKind::Expr,
        RECORD_FIELD => FragmentKind::Expr,
        CALL_EXPR => FragmentKind::Expr,
        INDEX_EXPR => FragmentKind::Expr,
        METHOD_CALL_EXPR => FragmentKind::Expr,
        AWAIT_EXPR => FragmentKind::Expr,
        CAST_EXPR => FragmentKind::Expr,
        REF_EXPR => FragmentKind::Expr,
        PREFIX_EXPR => FragmentKind::Expr,
        RANGE_EXPR => FragmentKind::Expr,
        BIN_EXPR => FragmentKind::Expr,
        _ => {
            // Unknown , Just guess it is `Items`
            FragmentKind::Items
        }
    }
}
<|endoftext|>


<commit_before>use libc::{c_char, c_int, c_long, c_uchar};
use object::PyObject;

#[cfg_attr(windows, link(name="pythonXY"))] extern "C" {
    pub fn PyImport_GetMagicNumber() -> c_long;
    pub fn PyImport_GetMagicTag() -> *const c_char;
    pub fn PyImport_ExecCodeModule(name: *const c_char,
                                   co: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_ExecCodeModuleEx(name: *const c_char,
                                     co: *mut PyObject,
                                     pathname: *const c_char)
     -> *mut PyObject;
    pub fn PyImport_ExecCodeModuleWithPathnames(name: *const c_char,
                                                co: *mut PyObject,
                                                pathname:
                                                    *const c_char,
                                                cpathname:
                                                    *const c_char)
     -> *mut PyObject;
    pub fn PyImport_ExecCodeModuleObject(name: *mut PyObject,
                                         co: *mut PyObject,
                                         pathname: *mut PyObject,
                                         cpathname: *mut PyObject)
     -> *mut PyObject;
    pub fn PyImport_GetModuleDict() -> *mut PyObject;
    #[cfg(Py_3_7)]
    pub fn PyImport_GetModule(name: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_AddModuleObject(name: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_AddModule(name: *const c_char) -> *mut PyObject;
    pub fn PyImport_ImportModule(name: *const c_char)
     -> *mut PyObject;
    pub fn PyImport_ImportModuleNoBlock(name: *const c_char)
     -> *mut PyObject;
    pub fn PyImport_ImportModuleLevel(name: *const c_char,
                                      globals: *mut PyObject,
                                      locals: *mut PyObject,
                                      fromlist: *mut PyObject,
                                      level: c_int) -> *mut PyObject;
    pub fn PyImport_ImportModuleLevelObject(name: *mut PyObject,
                                            globals: *mut PyObject,
                                            locals: *mut PyObject,
                                            fromlist: *mut PyObject,
                                            level: c_int)
     -> *mut PyObject;
}

#[inline]
pub unsafe fn PyImport_ImportModuleEx(name: *const c_char,
                                      globals: *mut PyObject,
                                      locals: *mut PyObject,
                                      fromlist: *mut PyObject)
  -> *mut PyObject {
    PyImport_ImportModuleLevel(name, globals, locals, fromlist, 0)
}

#[cfg_attr(windows, link(name="pythonXY"))] extern "C" {
    pub fn PyImport_GetImporter(path: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_Import(name: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_ReloadModule(m: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_Cleanup() -> ();
    pub fn PyImport_ImportFrozenModuleObject(name: *mut PyObject)
     -> c_int;
    pub fn PyImport_ImportFrozenModule(name: *const c_char)
     -> c_int;

    // pub static mut PyNullImporter_Type: PyTypeObject; -- does not actually exist in shared library

    pub fn PyImport_AppendInittab(name: *const c_char,
                                  initfunc: Option<extern "C" fn() -> *mut PyObject>)
     -> c_int;
}

#[repr(C)]
#[derive(Copy, Clone)]
#[cfg(not(Py_LIMITED_API))]
pub struct _frozen {
    pub name: *const c_char,
    pub code: *const c_uchar,
    pub size: c_int,
}

#[cfg(not(Py_LIMITED_API))]
#[cfg_attr(windows, link(name="pythonXY"))]
extern "C" {
    pub static mut PyImport_FrozenModules: *const _frozen;
}
<commit_msg>Make initfunc argument to PyImport_AppendInittab() unsafe<commit_after>use libc::{c_char, c_int, c_long, c_uchar};
use object::PyObject;

#[cfg_attr(windows, link(name="pythonXY"))] extern "C" {
    pub fn PyImport_GetMagicNumber() -> c_long;
    pub fn PyImport_GetMagicTag() -> *const c_char;
    pub fn PyImport_ExecCodeModule(name: *const c_char,
                                   co: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_ExecCodeModuleEx(name: *const c_char,
                                     co: *mut PyObject,
                                     pathname: *const c_char)
     -> *mut PyObject;
    pub fn PyImport_ExecCodeModuleWithPathnames(name: *const c_char,
                                                co: *mut PyObject,
                                                pathname:
                                                    *const c_char,
                                                cpathname:
                                                    *const c_char)
     -> *mut PyObject;
    pub fn PyImport_ExecCodeModuleObject(name: *mut PyObject,
                                         co: *mut PyObject,
                                         pathname: *mut PyObject,
                                         cpathname: *mut PyObject)
     -> *mut PyObject;
    pub fn PyImport_GetModuleDict() -> *mut PyObject;
    #[cfg(Py_3_7)]
    pub fn PyImport_GetModule(name: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_AddModuleObject(name: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_AddModule(name: *const c_char) -> *mut PyObject;
    pub fn PyImport_ImportModule(name: *const c_char)
     -> *mut PyObject;
    pub fn PyImport_ImportModuleNoBlock(name: *const c_char)
     -> *mut PyObject;
    pub fn PyImport_ImportModuleLevel(name: *const c_char,
                                      globals: *mut PyObject,
                                      locals: *mut PyObject,
                                      fromlist: *mut PyObject,
                                      level: c_int) -> *mut PyObject;
    pub fn PyImport_ImportModuleLevelObject(name: *mut PyObject,
                                            globals: *mut PyObject,
                                            locals: *mut PyObject,
                                            fromlist: *mut PyObject,
                                            level: c_int)
     -> *mut PyObject;
}

#[inline]
pub unsafe fn PyImport_ImportModuleEx(name: *const c_char,
                                      globals: *mut PyObject,
                                      locals: *mut PyObject,
                                      fromlist: *mut PyObject)
  -> *mut PyObject {
    PyImport_ImportModuleLevel(name, globals, locals, fromlist, 0)
}

#[cfg_attr(windows, link(name="pythonXY"))] extern "C" {
    pub fn PyImport_GetImporter(path: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_Import(name: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_ReloadModule(m: *mut PyObject) -> *mut PyObject;
    pub fn PyImport_Cleanup() -> ();
    pub fn PyImport_ImportFrozenModuleObject(name: *mut PyObject)
     -> c_int;
    pub fn PyImport_ImportFrozenModule(name: *const c_char)
     -> c_int;

    // pub static mut PyNullImporter_Type: PyTypeObject; -- does not actually exist in shared library

    pub fn PyImport_AppendInittab(name: *const c_char,
                                  initfunc: Option<unsafe extern "C" fn() -> *mut PyObject>)
     -> c_int;
}

#[repr(C)]
#[derive(Copy, Clone)]
#[cfg(not(Py_LIMITED_API))]
pub struct _frozen {
    pub name: *const c_char,
    pub code: *const c_uchar,
    pub size: c_int,
}

#[cfg(not(Py_LIMITED_API))]
#[cfg_attr(windows, link(name="pythonXY"))]
extern "C" {
    pub static mut PyImport_FrozenModules: *const _frozen;
}
<|endoftext|>


<commit_before>use azure_sdk_core::errors::{check_status_extract_body, AzureError};
use hyper::{self, header, Body, StatusCode};
use hyper_rustls::HttpsConnector;
use ring::hmac;
use std::ops::Add;
use time::Duration;
use url::{form_urlencoded, Url};

mod client;
pub use self::client::Client;

type HttpClient = hyper::Client<HttpsConnector<hyper::client::HttpConnector>>;

#[inline]
fn send_event_prepare<B: Into<String>>(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    signing_key: &hmac::Key,
    event_body: B,
    duration: Duration,
) -> Result<hyper::client::ResponseFuture, AzureError> {
    // prepare the url to call
    let url = format!(
        "https://{}.servicebus.windows.net/{}/messages",
        namespace, event_hub
    );
    debug!("url == {:?}", url);

    // generate sas signature based on key name, key value, url and duration.
    let sas = generate_signature(policy_name, signing_key, &url, duration);
    debug!("sas == {}", sas);

    let event_body = event_body.into();
    let request = hyper::Request::post(url)
        .header(header::AUTHORIZATION, sas)
        .body(event_body.into())?;

    Ok(http_client.request(request))
}

fn peek_lock_prepare(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    signing_key: &hmac::Key,
    duration: Duration,
    timeout: Option<Duration>,
) -> Result<hyper::client::ResponseFuture, AzureError> {
    // prepare the url to call
    let mut url = Url::parse(&format!(
        "https://{}.servicebus.windows.net/{}/messages/head",
        namespace, event_hub
    ))?;
    if let Some(t) = timeout {
        url.query_pairs_mut()
            .append_pair("timeout", &t.num_seconds().to_string());
    }
    debug!("url == {:?}", url);

    // generate sas signature based on key name, key value, url and duration.
    let sas = generate_signature(policy_name, signing_key, &url.as_str(), duration);
    debug!("sas == {}", sas);

    let request = hyper::Request::post(url.into_string())
        .header(header::AUTHORIZATION, sas)
        .body(Body::empty())?;

    Ok(http_client.request(request))
}

async fn peek_lock(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    hmac: &hmac::Key,
    duration: Duration,
    timeout: Option<Duration>,
) -> Result<String, AzureError> {
    let req = peek_lock_prepare(
        http_client,
        namespace,
        event_hub,
        policy_name,
        hmac,
        duration,
        timeout,
    );

    check_status_extract_body(req?, StatusCode::CREATED).await
}

fn receive_and_delete_prepare(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    signing_key: &hmac::Key,
    duration: Duration,
) -> Result<hyper::client::ResponseFuture, AzureError> {
    // prepare the url to call
    let url = format!(
        "https://{}.servicebus.windows.net/{}/messages/head",
        namespace, event_hub
    );
    debug!("url == {:?}", url);

    // generate sas signature based on key name, key value, url and duration.
    let sas = generate_signature(policy_name, signing_key, &url, duration);
    debug!("sas == {}", sas);

    let request = hyper::Request::delete(url)
        .header(header::AUTHORIZATION, sas)
        .body(Body::empty())?;

    Ok(http_client.request(request))
}

async fn receive_and_delete(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    hmac: &hmac::Key,
    duration: Duration,
) -> Result<String, AzureError> {
    let req = receive_and_delete_prepare(
        http_client,
        namespace,
        event_hub,
        policy_name,
        hmac,
        duration,
    );

    check_status_extract_body(req?, StatusCode::OK).await
}

fn delete_message_prepare(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    signing_key: &hmac::Key,
    duration: Duration,
    message_id: &str,
    lock_token: &str,
) -> Result<hyper::client::ResponseFuture, AzureError> {
    // prepare the url to call
    let url = format!(
        "https://{}.servicebus.windows.net/{}/messages/{}/{}",
        namespace, event_hub, message_id, lock_token
    );
    debug!("url == {:?}", url);

    // generate sas signature based on key name, key value, url and duration.
    let sas = generate_signature(policy_name, signing_key, &url, duration);
    debug!("sas == {}", sas);

    let request = hyper::Request::delete(url)
        .header(header::AUTHORIZATION, sas)
        .body(Body::empty())?;

    Ok(http_client.request(request))
}

async fn delete_message(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    hmac: &hmac::Key,
    duration: Duration,
    message_id: &str,
    lock_token: &str,
) -> Result<(), AzureError> {
    check_status_extract_body(
        delete_message_prepare(
            http_client,
            namespace,
            event_hub,
            policy_name,
            hmac,
            duration,
            message_id,
            lock_token,
        )?,
        StatusCode::OK,
    )
    .await?;
    Ok(())
}

fn unlock_message_prepare(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    signing_key: &hmac::Key,
    duration: Duration,
    message_id: &str,
    lock_token: &str,
) -> Result<hyper::client::ResponseFuture, AzureError> {
    // prepare the url to call
    let url = format!(
        "https://{}.servicebus.windows.net/{}/messages/{}/{}",
        namespace, event_hub, message_id, lock_token
    );
    debug!("url == {:?}", url);

    // generate sas signature based on key name, key value, url and duration.
    let sas = generate_signature(policy_name, signing_key, &url, duration);
    debug!("sas == {}", sas);

    let request = hyper::Request::put(url)
        .header(header::AUTHORIZATION, sas)
        .body(Body::empty())?;

    Ok(http_client.request(request))
}

async fn unlock_message(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    hmac: &hmac::Key,
    duration: Duration,
    message_id: &str,
    lock_token: &str,
) -> Result<(), AzureError> {
    check_status_extract_body(
        unlock_message_prepare(
            http_client,
            namespace,
            event_hub,
            policy_name,
            hmac,
            duration,
            message_id,
            lock_token,
        )?,
        StatusCode::OK,
    )
    .await?;
    Ok(())
}

fn renew_lock_prepare(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    signing_key: &hmac::Key,
    duration: Duration,
    message_id: &str,
    lock_token: &str,
) -> Result<hyper::client::ResponseFuture, AzureError> {
    // prepare the url to call
    let url = format!(
        "https://{}.servicebus.windows.net/{}/messages/{}/{}",
        namespace, event_hub, message_id, lock_token
    );
    debug!("url == {:?}", url);

    // generate sas signature based on key name, key value, url and duration.
    let sas = generate_signature(policy_name, signing_key, &url, duration);
    debug!("sas == {}", sas);

    let request = hyper::Request::post(url)
        .header(header::AUTHORIZATION, sas)
        .body(Body::empty())?;

    Ok(http_client.request(request))
}

async fn renew_lock(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    hmac: &hmac::Key,
    duration: Duration,
    message_id: &str,
    lock_token: &str,
) -> Result<(), AzureError> {
    check_status_extract_body(
        renew_lock_prepare(
            http_client,
            namespace,
            event_hub,
            policy_name,
            hmac,
            duration,
            message_id,
            lock_token,
        )?,
        StatusCode::OK,
    )
    .await?;
    Ok(())
}

async fn send_event(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    hmac: &hmac::Key,
    event_body: &str,
    duration: Duration,
) -> Result<(), AzureError> {
    let req = send_event_prepare(
        http_client,
        namespace,
        event_hub,
        policy_name,
        hmac,
        event_body,
        duration,
    );

    check_status_extract_body(req?, StatusCode::CREATED).await?;
    Ok(())
}

fn generate_signature(
    policy_name: &str,
    signing_key: &hmac::Key,
    url: &str,
    ttl: Duration,
) -> String {
    use url::form_urlencoded::Serializer;

    let expiry = ::chrono::Utc::now().add(ttl).timestamp();
    debug!("expiry == {:?}", expiry);

    let url_encoded: String = form_urlencoded::byte_serialize(url.as_bytes()).collect();
    debug!("url_encoded == {:?}", url_encoded);

    let str_to_sign = format!("{}\n{}", url_encoded, expiry);
    debug!("str_to_sign == {:?}", str_to_sign);

    let sig = hmac::sign(signing_key, str_to_sign.as_bytes());
    let sig = {
        let sig = ::base64::encode(sig.as_ref());
        debug!("sig == {}", sig);
        let mut ser = Serializer::new(String::new());
        ser.append_pair("sig", &sig);
        let sig = ser.finish();
        debug!("sig == {}", sig);
        sig
    };

    debug!("sig == {:?}", sig);

    format!(
        "SharedAccessSignature sr={}&{}&se={}&skn={}",
        &url_encoded, sig, expiry, policy_name
    )
}
<commit_msg>Including necessary Content_Length header for peek_lock<commit_after>use azure_sdk_core::errors::{check_status_extract_body, AzureError};
use hyper::{self, header, Body, StatusCode};
use hyper_rustls::HttpsConnector;
use ring::hmac;
use std::ops::Add;
use time::Duration;
use url::{form_urlencoded, Url};

mod client;
pub use self::client::Client;

type HttpClient = hyper::Client<HttpsConnector<hyper::client::HttpConnector>>;

#[inline]
fn send_event_prepare<B: Into<String>>(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    signing_key: &hmac::Key,
    event_body: B,
    duration: Duration,
) -> Result<hyper::client::ResponseFuture, AzureError> {
    // prepare the url to call
    let url = format!(
        "https://{}.servicebus.windows.net/{}/messages",
        namespace, event_hub
    );
    debug!("url == {:?}", url);

    // generate sas signature based on key name, key value, url and duration.
    let sas = generate_signature(policy_name, signing_key, &url, duration);
    debug!("sas == {}", sas);

    let event_body = event_body.into();
    let request = hyper::Request::post(url)
        .header(header::AUTHORIZATION, sas)
        .body(event_body.into())?;

    Ok(http_client.request(request))
}

fn peek_lock_prepare(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    signing_key: &hmac::Key,
    duration: Duration,
    timeout: Option<Duration>,
) -> Result<hyper::client::ResponseFuture, AzureError> {
    // prepare the url to call
    let mut url = Url::parse(&format!(
        "https://{}.servicebus.windows.net/{}/messages/head",
        namespace, event_hub
    ))?;
    if let Some(t) = timeout {
        url.query_pairs_mut()
            .append_pair("timeout", &t.num_seconds().to_string());
    }
    debug!("url == {:?}", url);

    // generate sas signature based on key name, key value, url and duration.
    let sas = generate_signature(policy_name, signing_key, &url.as_str(), duration);
    debug!("sas == {}", sas);

    let request = hyper::Request::post(url.into_string())
        .header(header::AUTHORIZATION, sas)
        .header(header::CONTENT_LENGTH, 0)
        .body(Body::empty())?;

    Ok(http_client.request(request))
}

async fn peek_lock(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    hmac: &hmac::Key,
    duration: Duration,
    timeout: Option<Duration>,
) -> Result<String, AzureError> {
    let req = peek_lock_prepare(
        http_client,
        namespace,
        event_hub,
        policy_name,
        hmac,
        duration,
        timeout,
    );

    check_status_extract_body(req?, StatusCode::CREATED).await
}

fn receive_and_delete_prepare(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    signing_key: &hmac::Key,
    duration: Duration,
) -> Result<hyper::client::ResponseFuture, AzureError> {
    // prepare the url to call
    let url = format!(
        "https://{}.servicebus.windows.net/{}/messages/head",
        namespace, event_hub
    );
    debug!("url == {:?}", url);

    // generate sas signature based on key name, key value, url and duration.
    let sas = generate_signature(policy_name, signing_key, &url, duration);
    debug!("sas == {}", sas);

    let request = hyper::Request::delete(url)
        .header(header::AUTHORIZATION, sas)
        .body(Body::empty())?;

    Ok(http_client.request(request))
}

async fn receive_and_delete(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    hmac: &hmac::Key,
    duration: Duration,
) -> Result<String, AzureError> {
    let req = receive_and_delete_prepare(
        http_client,
        namespace,
        event_hub,
        policy_name,
        hmac,
        duration,
    );

    check_status_extract_body(req?, StatusCode::OK).await
}

fn delete_message_prepare(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    signing_key: &hmac::Key,
    duration: Duration,
    message_id: &str,
    lock_token: &str,
) -> Result<hyper::client::ResponseFuture, AzureError> {
    // prepare the url to call
    let url = format!(
        "https://{}.servicebus.windows.net/{}/messages/{}/{}",
        namespace, event_hub, message_id, lock_token
    );
    debug!("url == {:?}", url);

    // generate sas signature based on key name, key value, url and duration.
    let sas = generate_signature(policy_name, signing_key, &url, duration);
    debug!("sas == {}", sas);

    let request = hyper::Request::delete(url)
        .header(header::AUTHORIZATION, sas)
        .body(Body::empty())?;

    Ok(http_client.request(request))
}

async fn delete_message(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    hmac: &hmac::Key,
    duration: Duration,
    message_id: &str,
    lock_token: &str,
) -> Result<(), AzureError> {
    check_status_extract_body(
        delete_message_prepare(
            http_client,
            namespace,
            event_hub,
            policy_name,
            hmac,
            duration,
            message_id,
            lock_token,
        )?,
        StatusCode::OK,
    )
    .await?;
    Ok(())
}

fn unlock_message_prepare(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    signing_key: &hmac::Key,
    duration: Duration,
    message_id: &str,
    lock_token: &str,
) -> Result<hyper::client::ResponseFuture, AzureError> {
    // prepare the url to call
    let url = format!(
        "https://{}.servicebus.windows.net/{}/messages/{}/{}",
        namespace, event_hub, message_id, lock_token
    );
    debug!("url == {:?}", url);

    // generate sas signature based on key name, key value, url and duration.
    let sas = generate_signature(policy_name, signing_key, &url, duration);
    debug!("sas == {}", sas);

    let request = hyper::Request::put(url)
        .header(header::AUTHORIZATION, sas)
        .body(Body::empty())?;

    Ok(http_client.request(request))
}

async fn unlock_message(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    hmac: &hmac::Key,
    duration: Duration,
    message_id: &str,
    lock_token: &str,
) -> Result<(), AzureError> {
    check_status_extract_body(
        unlock_message_prepare(
            http_client,
            namespace,
            event_hub,
            policy_name,
            hmac,
            duration,
            message_id,
            lock_token,
        )?,
        StatusCode::OK,
    )
    .await?;
    Ok(())
}

fn renew_lock_prepare(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    signing_key: &hmac::Key,
    duration: Duration,
    message_id: &str,
    lock_token: &str,
) -> Result<hyper::client::ResponseFuture, AzureError> {
    // prepare the url to call
    let url = format!(
        "https://{}.servicebus.windows.net/{}/messages/{}/{}",
        namespace, event_hub, message_id, lock_token
    );
    debug!("url == {:?}", url);

    // generate sas signature based on key name, key value, url and duration.
    let sas = generate_signature(policy_name, signing_key, &url, duration);
    debug!("sas == {}", sas);

    let request = hyper::Request::post(url)
        .header(header::AUTHORIZATION, sas)
        .body(Body::empty())?;

    Ok(http_client.request(request))
}

async fn renew_lock(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    hmac: &hmac::Key,
    duration: Duration,
    message_id: &str,
    lock_token: &str,
) -> Result<(), AzureError> {
    check_status_extract_body(
        renew_lock_prepare(
            http_client,
            namespace,
            event_hub,
            policy_name,
            hmac,
            duration,
            message_id,
            lock_token,
        )?,
        StatusCode::OK,
    )
    .await?;
    Ok(())
}

async fn send_event(
    http_client: &HttpClient,
    namespace: &str,
    event_hub: &str,
    policy_name: &str,
    hmac: &hmac::Key,
    event_body: &str,
    duration: Duration,
) -> Result<(), AzureError> {
    let req = send_event_prepare(
        http_client,
        namespace,
        event_hub,
        policy_name,
        hmac,
        event_body,
        duration,
    );

    check_status_extract_body(req?, StatusCode::CREATED).await?;
    Ok(())
}

fn generate_signature(
    policy_name: &str,
    signing_key: &hmac::Key,
    url: &str,
    ttl: Duration,
) -> String {
    use url::form_urlencoded::Serializer;

    let expiry = ::chrono::Utc::now().add(ttl).timestamp();
    debug!("expiry == {:?}", expiry);

    let url_encoded: String = form_urlencoded::byte_serialize(url.as_bytes()).collect();
    debug!("url_encoded == {:?}", url_encoded);

    let str_to_sign = format!("{}\n{}", url_encoded, expiry);
    debug!("str_to_sign == {:?}", str_to_sign);

    let sig = hmac::sign(signing_key, str_to_sign.as_bytes());
    let sig = {
        let sig = ::base64::encode(sig.as_ref());
        debug!("sig == {}", sig);
        let mut ser = Serializer::new(String::new());
        ser.append_pair("sig", &sig);
        let sig = ser.finish();
        debug!("sig == {}", sig);
        sig
    };

    debug!("sig == {:?}", sig);

    format!(
        "SharedAccessSignature sr={}&{}&se={}&skn={}",
        &url_encoded, sig, expiry, policy_name
    )
}
<|endoftext|>