<commit_before>extern crate euclid;
extern crate image;

extern crate glium;
extern crate vitral;

use euclid::{Rect, Point2D, point2, Size2D, rect, vec2};
use glium::glutin;
use std::path::Path;
use vitral::{Align, ButtonAction, RectUtil};
use vitral::glium_backend as backend;

use backend::{Backend, DefaultVertex, TextureHandle};

type Core = vitral::Core<TextureHandle, DefaultVertex>;

pub fn clamp<C: PartialOrd + Copy>(mn: C, mx: C, x: C) -> C {
    if x < mn {
        mn
    } else if x > mx {
        mx
    } else {
        x
    }
}

fn _load_image<V>(backend: &mut backend::Backend<V>, path: &str) -> vitral::ImageData<usize>
where
    V: vitral::Vertex + glium::Vertex,
{
    let image: vitral::ImageBuffer = image::open(&Path::new(path)).unwrap().into();
    let size = image.size;
    let texture = backend.make_texture(image);

    vitral::ImageData {
        texture,
        size,
        tex_coords: Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1.0, 1.0)),
    }
}

struct App {
    core: Core,
    font: vitral::FontData<TextureHandle>,
    fore_color: [f32; 4],
    back_color: [f32; 4],
    bounds: Rect<f32>,
}

impl App {
    pub fn new(core: Core, font: vitral::FontData<TextureHandle>) -> App {
        let bounds = core.bounds();
        App {
            core,
            font,
            fore_color: [1.0, 0.5, 0.1, 1.0],
            back_color: [0.0, 0.0, 0.0, 1.0],
            bounds
        }
    }

    fn bright_color(&self) -> [f32; 4] {
        [clamp(0.0, 1.0, self.fore_color[0] * 1.5),
         clamp(0.0, 1.0, self.fore_color[1] * 1.5),
         clamp(0.0, 1.0, self.fore_color[2] * 1.5),
         1.0]
    }

    fn begin(&mut self) { self.core.begin_frame(); }

    fn outline_text(&mut self, pos: Point2D<f32>, text: &str) -> Point2D<f32> {
        for offset in &[
            vec2(-1.0, 0.0),
            vec2(1.0, 0.0),
            vec2(0.0, -1.0),
            vec2(0.0, 1.0),
        ]
        {
            self.core.draw_text(
                &self.font,
                pos + *offset,
                Align::Left,
                self.back_color,
                text,
            );
        }

        self.core.draw_text(
            &self.font,
            pos,
            Align::Left,
            self.fore_color,
            text,
        )
    }

    fn title_bar(&mut self, bounds: &Rect<f32>, text: &str) {
        self.core.fill_rect(bounds, self.back_color);
        {
            let bounds = bounds.inclusivize();
            self.core.draw_line(
                1.0,
                self.fore_color,
                bounds.bottom_left(),
                bounds.bottom_right());
        }

        // Margin
        let bounds = bounds.inflate(-2.0, -2.0);

        self.core.draw_text(
            &self.font,
            bounds.anchor(&point2(0.0, -1.0)),
            Align::Center,
            self.fore_color,
            text);
    }

    fn quit_button(&mut self, bounds: &Rect<f32>) -> bool {
        let click_state = self.core.click_state(bounds);

        let color = if click_state != ButtonAction::Inert {
            self.bright_color()
        } else {
            self.fore_color
        };

        self.core.fill_rect(bounds, color);
        self.core.fill_rect(&bounds.inflate(-1.0, -1.0), self.back_color);

        let inner = bounds.inflate(-3.0, -3.0).inclusivize();

        self.core.draw_line(
            1.0,
            color,
            inner.bottom_right(),
            inner.origin);

        self.core.draw_line(
            1.0,
            color,
            inner.top_right(),
            inner.bottom_left());

        self.core.click_state(bounds) == ButtonAction::LeftClicked
    }

    fn render(&mut self) {
        self.core.fill_rect(
            &rect(0.0, 20.0, 128.0, 128.0),
            [0.4, 0.5, 0.5, 1.0],
        );
        self.outline_text(point2(10.0, 30.0), "Hello, world!");
    }
}

fn main() {
    let size = Size2D::new(640.0, 360.0);
    let mut backend: Backend<DefaultVertex> = backend::start(
        size.width as u32,
        size.height as u32,
        "Vitral Demo",
        backend::DEFAULT_SHADER,
    ).expect("Failed to start Glium backend!");

    let core = vitral::Builder::new().build(size, |img| backend.make_texture(img));
    let font = vitral::build_default_font(|img| backend.make_texture(img));
    let mut app = App::new(core, font);

    loop {
        app.begin();
        app.render();
        let (_, title_area) = app.bounds.horizontal_split(12.0);
        app.title_bar(&title_area, "Vitral Demo");

        let (_, widget_area) = title_area.vertical_split(-12.0);
        if app.quit_button(&widget_area) {
            return;
        }

        while let Some(k) = backend.poll_key() {
            if k.key_code == glutin::VirtualKeyCode::Q {
                return;
            }
        }

        if !backend.update(&mut app.core) {
            break;
        }
    }
}
<commit_msg>Example builds without feature flag<commit_after>extern crate euclid;
extern crate image;

#[cfg(feature = "glium_backend")]
extern crate glium;
extern crate vitral;

fn main() { feature::main(); }

#[cfg(feature = "glium_backend")]
mod feature {
    use image;
    use vitral::glium_backend as backend;
    use vitral::glium_backend::{DefaultVertex, TextureHandle};
    use euclid::{Rect, Point2D, point2, Size2D, vec2};
    use glium::{self, glutin};
    use std::path::Path;
    use vitral::{self, Align, ButtonAction, RectUtil};

    type Core = vitral::Core<TextureHandle, DefaultVertex>;
    type Backend = backend::Backend<DefaultVertex>;

    pub fn clamp<C: PartialOrd + Copy>(mn: C, mx: C, x: C) -> C {
        if x < mn {
            mn
        } else if x > mx {
            mx
        } else {
            x
        }
    }

    fn load_image<V>(backend: &mut backend::Backend<V>, path: &str) -> vitral::ImageData<usize>
    where
        V: vitral::Vertex + glium::Vertex,
    {
        let image: vitral::ImageBuffer = image::open(&Path::new(path)).unwrap().into();
        let size = image.size;
        let texture = backend.make_texture(image);

        vitral::ImageData {
            texture,
            size,
            tex_coords: Rect::new(Point2D::new(0.0, 0.0), Size2D::new(1.0, 1.0)),
        }
    }

    struct App {
        core: Core,
        font: vitral::FontData<TextureHandle>,
        image: vitral::ImageData<TextureHandle>,
        fore_color: [f32; 4],
        back_color: [f32; 4],
        bounds: Rect<f32>,
    }

    impl App {
        pub fn new(core: Core, backend: &mut Backend) -> App {
            let bounds = core.bounds();
            let font = vitral::build_default_font(|img| backend.make_texture(img));
            let image = load_image(backend, "julia.png");
            App {
                core,
                font,
                image,
                fore_color: [1.0, 0.5, 0.1, 1.0],
                back_color: [0.0, 0.0, 0.0, 1.0],
                bounds,
            }
        }

        fn bright_color(&self) -> [f32; 4] {
            [
                clamp(0.0, 1.0, self.fore_color[0] * 1.5),
                clamp(0.0, 1.0, self.fore_color[1] * 1.5),
                clamp(0.0, 1.0, self.fore_color[2] * 1.5),
                1.0,
            ]
        }

        fn begin(&mut self) { self.core.begin_frame(); }

        fn outline_text(&mut self, pos: Point2D<f32>, text: &str) -> Point2D<f32> {
            for offset in &[
                vec2(-1.0, 0.0),
                vec2(1.0, 0.0),
                vec2(0.0, -1.0),
                vec2(0.0, 1.0),
            ]
            {
                self.core.draw_text(
                    &self.font,
                    pos + *offset,
                    Align::Left,
                    self.back_color,
                    text,
                );
            }

            self.core.draw_text(
                &self.font,
                pos,
                Align::Left,
                self.fore_color,
                text,
            )
        }

        fn title_bar(&mut self, bounds: &Rect<f32>, text: &str) {
            self.core.fill_rect(bounds, self.back_color);
            {
                let bounds = bounds.inclusivize();
                self.core.draw_line(
                    1.0,
                    self.fore_color,
                    bounds.bottom_left(),
                    bounds.bottom_right(),
                );
            }

            // Margin
            let bounds = bounds.inflate(-2.0, -2.0);

            self.core.draw_text(
                &self.font,
                bounds.anchor(&point2(0.0, -1.0)),
                Align::Center,
                self.fore_color,
                text,
            );
        }

        fn quit_button(&mut self, bounds: &Rect<f32>) -> bool {
            let click_state = self.core.click_state(bounds);

            let color = if click_state != ButtonAction::Inert {
                self.bright_color()
            } else {
                self.fore_color
            };

            self.core.fill_rect(bounds, color);
            self.core.fill_rect(
                &bounds.inflate(-1.0, -1.0),
                self.back_color,
            );

            let inner = bounds.inflate(-3.0, -3.0).inclusivize();

            self.core.draw_line(
                1.0,
                color,
                inner.bottom_right(),
                inner.origin,
            );

            self.core.draw_line(
                1.0,
                color,
                inner.top_right(),
                inner.bottom_left(),
            );

            self.core.click_state(bounds) == ButtonAction::LeftClicked
        }

        fn render(&mut self) {
            self.core.draw_image(&self.image, point2(20.0, 20.0), [1.0, 1.0, 1.0, 1.0]);
            self.outline_text(point2(22.0, 22.0), "Hello, world!");
        }
    }

    pub fn main() {
        let size = Size2D::new(640.0, 360.0);
        let mut backend: Backend = backend::start(
            size.width as u32,
            size.height as u32,
            "Vitral Demo",
            backend::DEFAULT_SHADER,
        ).expect("Failed to start Glium backend!");

        let core = vitral::Builder::new().build(size, |img| backend.make_texture(img));
        let mut app = App::new(core, &mut backend);

        loop {
            app.begin();
            app.render();
            let (_, title_area) = app.bounds.horizontal_split(12.0);
            app.title_bar(&title_area, "Vitral Demo");

            let (_, widget_area) = title_area.vertical_split(-12.0);
            if app.quit_button(&widget_area) {
                return;
            }

            while let Some(k) = backend.poll_key() {
                if k.key_code == glutin::VirtualKeyCode::Q {
                    return;
                }

            }

            if !backend.update(&mut app.core) {
                break;
            }
        }
    }
}

#[cfg(not(feature = "glium_backend"))]
mod feature {
    pub fn main() {
        println!("Try running `cargo run --features \"glium_backend\" --example <example_name>");
    }
}
<|endoftext|>


<commit_before>use clap::{App, Arg};
use dicom_ul::pdu::reader::{DEFAULT_MAX_PDU, MINIMUM_PDU_SIZE, read_pdu};
use dicom_ul::pdu::writer::write_pdu;
use dicom_ul::pdu::Pdu;
use snafu::{Backtrace, ErrorCompat, OptionExt, ResultExt, Snafu};
use std::io::Write;
use std::net::{Shutdown, TcpListener, TcpStream};
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
use std::thread;
use std::thread::JoinHandle;

type Result<T> = std::result::Result<T, Error>;

#[derive(Debug, Snafu)]
#[non_exhaustive]
enum Error {
    #[snafu(display("Could not clone socket"))]
    CloneSocket {
        backtrace: Backtrace,
        source: std::io::Error,
    },
    #[snafu(display("Could not send message"))]
    SendMessage {
        backtrace: Backtrace,
        source: std::sync::mpsc::SendError<ThreadMessage>,
    },
    #[snafu(display("Could not receive message"))]
    ReceiveMessage {
        backtrace: Backtrace,
        source: std::sync::mpsc::RecvError,
    },
    #[snafu(display("Could not close socket"))]
    CloseSocket {
        backtrace: Backtrace,
        source: std::io::Error,
    },
    #[snafu(display("Could not connect to destination SCP"))]
    Connect {
        backtrace: Backtrace,
        source: std::io::Error,
    },
    #[snafu(display("SCP reader thread panicked"))]
    ScpReaderPanic { backtrace: Backtrace },
    #[snafu(display("SCU reader thread panicked"))]
    ScuReaderPanic { backtrace: Backtrace },
}

fn report<E: 'static>(err: E)
where
    E: std::error::Error,
    E: ErrorCompat,
{
    eprintln!("[ERROR] {}", err);
    if let Some(source) = err.source() {
        eprintln!();
        eprintln!("Caused by:");
        for (i, e) in std::iter::successors(Some(source), |e| e.source()).enumerate() {
            eprintln!("   {}: {}", i, e);
        }
    }

    let env_backtrace = std::env::var("RUST_BACKTRACE").unwrap_or_default();
    let env_lib_backtrace = std::env::var("RUST_LIB_BACKTRACE").unwrap_or_default();
    if env_lib_backtrace == "1" || (env_backtrace == "1" && env_lib_backtrace != "0") {
        if let Some(backtrace) = ErrorCompat::backtrace(&err) {
            eprintln!();
            eprintln!("Backtrace:");
            eprintln!("{}", backtrace);
        }
    }
}

#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ProviderType {
    /// Service class provider
    Scp,
    /// Service class user
    Scu,
}

#[derive(Debug)]
pub enum ThreadMessage {
    SendPdu {
        to: ProviderType,
        pdu: Pdu,
    },
    ReadErr {
        from: ProviderType,
        err: dicom_ul::pdu::reader::Error,
    },
    WriteErr {
        from: ProviderType,
        err: dicom_ul::pdu::writer::Error,
    },
    Shutdown {
        initiator: ProviderType,
    },
}

fn run(scu_stream: &mut TcpStream, destination_addr: &str, strict: bool, verbose: bool) -> Result<()> {
    // Before we do anything, let's also open another connection to the destination
    // SCP.
    match TcpStream::connect(destination_addr) {
        Ok(ref mut scp_stream) => {
            let (message_tx, message_rx): (Sender<ThreadMessage>, Receiver<ThreadMessage>) =
                mpsc::channel();

            let scu_reader_thread: JoinHandle<Result<()>>;
            let scp_reader_thread: JoinHandle<Result<()>>;

            {
                let mut reader = scu_stream.try_clone().context(CloneSocket)?;
                let message_tx = message_tx.clone();
                scu_reader_thread = thread::spawn(move || {
                    loop {
                        match read_pdu(&mut reader, DEFAULT_MAX_PDU, strict) {
                            Ok(pdu) => {
                                message_tx
                                    .send(ThreadMessage::SendPdu {
                                        to: ProviderType::Scp,
                                        pdu,
                                    })
                                    .context(SendMessage)?;
                            }
                            Err(dicom_ul::pdu::reader::Error::NoPduAvailable { .. }) => {
                                message_tx
                                    .send(ThreadMessage::Shutdown {
                                        initiator: ProviderType::Scu,
                                    })
                                    .context(SendMessage)?;
                                break;
                            }
                            Err(err) => {
                                message_tx
                                    .send(ThreadMessage::ReadErr {
                                        from: ProviderType::Scu,
                                        err,
                                    })
                                    .context(SendMessage)?;
                                break;
                            }
                        }
                    }

                    Ok(())
                });
            }

            {
                let mut reader = scp_stream.try_clone().context(CloneSocket)?;
                scp_reader_thread = thread::spawn(move || {
                    loop {
                        match read_pdu(&mut reader, DEFAULT_MAX_PDU, strict) {
                            Ok(pdu) => {
                                message_tx
                                    .send(ThreadMessage::SendPdu {
                                        to: ProviderType::Scu,
                                        pdu,
                                    })
                                    .context(SendMessage)?;
                            }
                            Err(dicom_ul::pdu::reader::Error::NoPduAvailable { .. }) => {
                                message_tx
                                    .send(ThreadMessage::Shutdown {
                                        initiator: ProviderType::Scp,
                                    })
                                    .context(SendMessage)?;
                                break;
                            }
                            Err(err) => {
                                message_tx
                                    .send(ThreadMessage::ReadErr {
                                        from: ProviderType::Scp,
                                        err,
                                    })
                                    .context(SendMessage)?;
                                break;
                            }
                        }
                    }

                    Ok(())
                });
            }
            let mut buffer: Vec<u8> = Vec::with_capacity(MINIMUM_PDU_SIZE as usize);

            loop {
                let message = message_rx.recv().context(ReceiveMessage)?;
                match message {
                    ThreadMessage::SendPdu { to, pdu } => match to {
                        ProviderType::Scu => {
                            if verbose {
                                println!("scu <---- scp: {}", pdu.short_description());
                            }
                            buffer.clear();
                            write_pdu(&mut buffer, &pdu).unwrap();
                            scu_stream.write_all(&buffer).unwrap();
                        }
                        ProviderType::Scp => {
                            if verbose {
                                println!("scu ----> scp: {}", pdu.short_description());
                            }
                            buffer.clear();
                            write_pdu(&mut buffer, &pdu).unwrap();
                            scp_stream.write_all(&buffer).unwrap();
                        }
                    },
                    ThreadMessage::ReadErr { from, err } => {
                        eprintln!("error reading from {:?}:", from);
                        report(err);
                        break;
                    }
                    ThreadMessage::WriteErr { from, err } => {
                        eprintln!("error writing to {:?}", from);
                        report(err);
                        break;
                    }
                    ThreadMessage::Shutdown { initiator } => {
                        if verbose {
                            println!("shutdown initiated from: {:?}", initiator);
                        }
                        break;
                    }
                }
            }

            scu_stream.shutdown(Shutdown::Read).context(CloseSocket)?;
            scu_reader_thread.join().ok().context(ScuReaderPanic)??;

            scp_stream.shutdown(Shutdown::Read).context(CloseSocket)?;
            scp_reader_thread.join().ok().context(ScpReaderPanic)??;

            Ok(())
        }
        Err(e) => {
            scu_stream.shutdown(Shutdown::Both).context(CloseSocket)?;
            Err(e).context(Connect)
        }
    }
}

fn main() {
    let matches = App::new("scpproxy")
        .arg(
            Arg::with_name("destination-host")
                .help("The destination host name (SCP)")
                .required(true)
                .index(1),
        )
        .arg(
            Arg::with_name("destination-port")
                .help("The destination host port (SCP)")
                .required(true)
                .index(2),
        )
        .arg(
            Arg::with_name("listen-port")
                .help("The port that we will listen for SCU connections on")
                .short("-lp")
                .long("--listen-port")
                .default_value("3333")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("strict")
                .help("Enforce max PDU length")
                .short("-s")
                .long("--strict")
                .required(false)
                .takes_value(false)
        )
        .arg(
            Arg::with_name("verbose")
                .help("Verbose")
                .short("-v")
                .long("--verbose")
                .takes_value(false)
        )
        .get_matches();

    let destination_host = matches.value_of("destination-host").unwrap();
    let destination_port = matches.value_of("destination-port").unwrap();
    let listen_port = matches.value_of("listen-port").unwrap();
    let strict: bool = matches.is_present("strict");
    let verbose = matches.is_present("verbose");

    let listen_addr = format!("0.0.0.0:{}", listen_port);
    let destination_addr = format!("{}:{}", destination_host, destination_port);

    let listener = TcpListener::bind(&listen_addr).unwrap();
    if verbose {
        println!("listening on: {}", listen_addr);
        println!("forwarding to: {}", destination_addr);
    }
    for mut stream in listener.incoming() {
        match stream {
            Ok(ref mut scu_stream) => {
                if let Err(e) = run(scu_stream, &destination_addr, strict, verbose) {
                    report(e);
                }
            }
            Err(e) => {
                eprintln!("[ERROR] {}", e);
            }
        }
    }
}
<commit_msg>[scpproxy] add option --max-pdu-length<commit_after>use clap::{App, Arg};
use dicom_ul::pdu::reader::{DEFAULT_MAX_PDU, read_pdu};
use dicom_ul::pdu::writer::write_pdu;
use dicom_ul::pdu::Pdu;
use snafu::{Backtrace, ErrorCompat, OptionExt, ResultExt, Snafu};
use std::io::Write;
use std::net::{Shutdown, TcpListener, TcpStream};
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
use std::thread;
use std::thread::JoinHandle;

type Result<T> = std::result::Result<T, Error>;

#[derive(Debug, Snafu)]
#[non_exhaustive]
enum Error {
    #[snafu(display("Could not clone socket"))]
    CloneSocket {
        backtrace: Backtrace,
        source: std::io::Error,
    },
    #[snafu(display("Could not send message"))]
    SendMessage {
        backtrace: Backtrace,
        source: std::sync::mpsc::SendError<ThreadMessage>,
    },
    #[snafu(display("Could not receive message"))]
    ReceiveMessage {
        backtrace: Backtrace,
        source: std::sync::mpsc::RecvError,
    },
    #[snafu(display("Could not close socket"))]
    CloseSocket {
        backtrace: Backtrace,
        source: std::io::Error,
    },
    #[snafu(display("Could not connect to destination SCP"))]
    Connect {
        backtrace: Backtrace,
        source: std::io::Error,
    },
    #[snafu(display("SCP reader thread panicked"))]
    ScpReaderPanic { backtrace: Backtrace },
    #[snafu(display("SCU reader thread panicked"))]
    ScuReaderPanic { backtrace: Backtrace },
}

fn report<E: 'static>(err: E)
where
    E: std::error::Error,
    E: ErrorCompat,
{
    eprintln!("[ERROR] {}", err);
    if let Some(source) = err.source() {
        eprintln!();
        eprintln!("Caused by:");
        for (i, e) in std::iter::successors(Some(source), |e| e.source()).enumerate() {
            eprintln!("   {}: {}", i, e);
        }
    }

    let env_backtrace = std::env::var("RUST_BACKTRACE").unwrap_or_default();
    let env_lib_backtrace = std::env::var("RUST_LIB_BACKTRACE").unwrap_or_default();
    if env_lib_backtrace == "1" || (env_backtrace == "1" && env_lib_backtrace != "0") {
        if let Some(backtrace) = ErrorCompat::backtrace(&err) {
            eprintln!();
            eprintln!("Backtrace:");
            eprintln!("{}", backtrace);
        }
    }
}

#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ProviderType {
    /// Service class provider
    Scp,
    /// Service class user
    Scu,
}

#[derive(Debug)]
pub enum ThreadMessage {
    SendPdu {
        to: ProviderType,
        pdu: Pdu,
    },
    ReadErr {
        from: ProviderType,
        err: dicom_ul::pdu::reader::Error,
    },
    WriteErr {
        from: ProviderType,
        err: dicom_ul::pdu::writer::Error,
    },
    Shutdown {
        initiator: ProviderType,
    },
}

fn run(scu_stream: &mut TcpStream, destination_addr: &str, strict: bool, verbose: bool, max_pdu_length: u32 ) -> Result<()> {
    // Before we do anything, let's also open another connection to the destination
    // SCP.
    match TcpStream::connect(destination_addr) {
        Ok(ref mut scp_stream) => {
            let (message_tx, message_rx): (Sender<ThreadMessage>, Receiver<ThreadMessage>) =
                mpsc::channel();

            let scu_reader_thread: JoinHandle<Result<()>>;
            let scp_reader_thread: JoinHandle<Result<()>>;

            {
                let mut reader = scu_stream.try_clone().context(CloneSocket)?;
                let message_tx = message_tx.clone();
                scu_reader_thread = thread::spawn(move || {
                    loop {
                        match read_pdu(&mut reader, max_pdu_length, strict) {
                            Ok(pdu) => {
                                message_tx
                                    .send(ThreadMessage::SendPdu {
                                        to: ProviderType::Scp,
                                        pdu,
                                    })
                                    .context(SendMessage)?;
                            }
                            Err(dicom_ul::pdu::reader::Error::NoPduAvailable { .. }) => {
                                message_tx
                                    .send(ThreadMessage::Shutdown {
                                        initiator: ProviderType::Scu,
                                    })
                                    .context(SendMessage)?;
                                break;
                            }
                            Err(err) => {
                                message_tx
                                    .send(ThreadMessage::ReadErr {
                                        from: ProviderType::Scu,
                                        err,
                                    })
                                    .context(SendMessage)?;
                                break;
                            }
                        }
                    }

                    Ok(())
                });
            }

            {
                let mut reader = scp_stream.try_clone().context(CloneSocket)?;
                scp_reader_thread = thread::spawn(move || {
                    loop {
                        match read_pdu(&mut reader, max_pdu_length, strict) {
                            Ok(pdu) => {
                                message_tx
                                    .send(ThreadMessage::SendPdu {
                                        to: ProviderType::Scu,
                                        pdu,
                                    })
                                    .context(SendMessage)?;
                            }
                            Err(dicom_ul::pdu::reader::Error::NoPduAvailable { .. }) => {
                                message_tx
                                    .send(ThreadMessage::Shutdown {
                                        initiator: ProviderType::Scp,
                                    })
                                    .context(SendMessage)?;
                                break;
                            }
                            Err(err) => {
                                message_tx
                                    .send(ThreadMessage::ReadErr {
                                        from: ProviderType::Scp,
                                        err,
                                    })
                                    .context(SendMessage)?;
                                break;
                            }
                        }
                    }

                    Ok(())
                });
            }
            let mut buffer: Vec<u8> = Vec::with_capacity(max_pdu_length as usize);

            loop {
                let message = message_rx.recv().context(ReceiveMessage)?;
                match message {
                    ThreadMessage::SendPdu { to, pdu } => match to {
                        ProviderType::Scu => {
                            if verbose {
                                println!("scu <---- scp: {}", pdu.short_description());
                            }
                            buffer.clear();
                            write_pdu(&mut buffer, &pdu).unwrap();
                            scu_stream.write_all(&buffer).unwrap();
                        }
                        ProviderType::Scp => {
                            if verbose {
                                println!("scu ----> scp: {}", pdu.short_description());
                            }
                            buffer.clear();
                            write_pdu(&mut buffer, &pdu).unwrap();
                            scp_stream.write_all(&buffer).unwrap();
                        }
                    },
                    ThreadMessage::ReadErr { from, err } => {
                        eprintln!("error reading from {:?}:", from);
                        report(err);
                        break;
                    }
                    ThreadMessage::WriteErr { from, err } => {
                        eprintln!("error writing to {:?}", from);
                        report(err);
                        break;
                    }
                    ThreadMessage::Shutdown { initiator } => {
                        if verbose {
                            println!("shutdown initiated from: {:?}", initiator);
                        }
                        break;
                    }
                }
            }

            scu_stream.shutdown(Shutdown::Read).context(CloseSocket)?;
            scu_reader_thread.join().ok().context(ScuReaderPanic)??;

            scp_stream.shutdown(Shutdown::Read).context(CloseSocket)?;
            scp_reader_thread.join().ok().context(ScpReaderPanic)??;

            Ok(())
        }
        Err(e) => {
            scu_stream.shutdown(Shutdown::Both).context(CloseSocket)?;
            Err(e).context(Connect)
        }
    }
}

fn main() {
    let default_max = DEFAULT_MAX_PDU.to_string();
    let matches = App::new("scpproxy")
        .arg(
            Arg::with_name("destination-host")
                .help("The destination host name (SCP)")
                .required(true)
                .index(1),
        )
        .arg(
            Arg::with_name("destination-port")
                .help("The destination host port (SCP)")
                .required(true)
                .index(2),
        )
        .arg(
            Arg::with_name("listen-port")
                .help("The port that we will listen for SCU connections on")
                .short("-lp")
                .long("--listen-port")
                .default_value("3333")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("strict")
                .help("Enforce max PDU length")
                .short("-s")
                .long("--strict")
                .required(false)
                .takes_value(false)
        )
        .arg(
            Arg::with_name("verbose")
                .help("Verbose")
                .short("-v")
                .long("--verbose")
                .takes_value(false)
        )
        .arg(
            Arg::with_name("max-pdu-length")
                .help("Maximum PDU length")
                .short("-m")
                .long("--max-pdu-length")
                .default_value(&default_max)
                .takes_value(true)
        )
        .get_matches();

    let destination_host = matches.value_of("destination-host").unwrap();
    let destination_port = matches.value_of("destination-port").unwrap();
    let listen_port = matches.value_of("listen-port").unwrap();
    let strict: bool = matches.is_present("strict");
    let verbose = matches.is_present("verbose");
    let max_pdu_length: u32 = matches.value_of("max-pdu-length").unwrap().parse().unwrap();

    let listen_addr = format!("0.0.0.0:{}", listen_port);
    let destination_addr = format!("{}:{}", destination_host, destination_port);

    let listener = TcpListener::bind(&listen_addr).unwrap();
    if verbose {
        println!("listening on: {}", listen_addr);
        println!("forwarding to: {}", destination_addr);
    }
    for mut stream in listener.incoming() {
        match stream {
            Ok(ref mut scu_stream) => {
                if let Err(e) = run(scu_stream, &destination_addr, strict, verbose, max_pdu_length) {
                    report(e);
                }
            }
            Err(e) => {
                eprintln!("[ERROR] {}", e);
            }
        }
    }
}
<|endoftext|>


<commit_before>#![warn(bad_style,
        unused, unused_extern_crates, unused_import_braces,
        unused_qualifications, unused_results)]

extern crate twitter_api as twitter;
extern crate rustc_serialize as rustc_serialize;
extern crate oauth_client as oauth;

use std::io;
use std::io::prelude::*;
use std::env;
use std::fs::{File, OpenOptions};
use std::path::Path;
use std::path::PathBuf;
use rustc_serialize::Decodable;
use rustc_serialize::json::{self, Json};
use oauth::Token;

const TWITTER_CONF_FILENAME: &'static str = ".twitter.conf";

fn get_home_dir() -> PathBuf{
    match env::home_dir() {
        Some(p) => p,
        None => {
            panic!("Impossible to get your home dir!");
        }
    }
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct Config {
    pub consumer_key: String,
    pub consumer_secret: String,
    pub access_key: String,
    pub access_secret: String
}

impl Config {
    pub fn read(path_file : &Path) -> Option<Config> {
        let mut file = match File::open(path_file) {
            Ok(f) => f,
            Err(_) => return None
        };
        let conf = Json::from_reader(&mut file).unwrap();
        Decodable::decode(&mut json::Decoder::new(conf)).ok()
    }

    pub fn write(&self) {
        let path = Path::new(PATH);
        let mut file = match OpenOptions::new().write(true).open(&path) {
            Ok(f) => f,
            Err(e) => panic!("{}", e)
        };
        let _ = write!(&mut file, "{}\n", &json::encode(self).unwrap());
    }
}

fn console_input(prompt: &str) -> String {
    print!("{}\n\t", prompt);
    let mut line = String::new();
    let _ = io::stdin().read_line(&mut line).unwrap();
    line.trim().to_string()
}

fn main() {
    let conf = match Config::read() {
        Some(c) => c,
        None => {
            let consumer_key    = console_input("input your consumer key:");
            let consumer_secret = console_input("input your consumer secret:");
            let consumer = Token::new(consumer_key, consumer_secret);

            let request = twitter::get_request_token(&consumer);
            println!("open the following url:");
            println!("\t{}", twitter::get_authorize_url(&request));
            let pin = console_input("input pin:");
            let access = twitter::get_access_token(&consumer, &request, &pin);

            let c = Config {
                consumer_key: consumer.key.to_string(),
                consumer_secret: consumer.secret.to_string(),
                access_key: access.key.to_string(),
                access_secret: access.secret.to_string()
            };
            c.write();
            c
        }
    };

    let consumer = Token::new(conf.consumer_key, conf.consumer_secret);
    let access = Token::new(conf.access_key, conf.access_secret);

    let status = console_input("What's happening?");
    twitter::tweet(&consumer, &access, &status);
}

<commit_msg>examples/tweet.rs : write() takes now as argument the path of the twitter conf path<commit_after>#![warn(bad_style,
        unused, unused_extern_crates, unused_import_braces,
        unused_qualifications, unused_results)]

extern crate twitter_api as twitter;
extern crate rustc_serialize as rustc_serialize;
extern crate oauth_client as oauth;

use std::io;
use std::io::prelude::*;
use std::env;
use std::fs::{File, OpenOptions};
use std::path::Path;
use std::path::PathBuf;
use rustc_serialize::Decodable;
use rustc_serialize::json::{self, Json};
use oauth::Token;

const TWITTER_CONF_FILENAME: &'static str = ".twitter.conf";

fn get_home_dir() -> PathBuf{
    match env::home_dir() {
        Some(p) => p,
        None => {
            panic!("Impossible to get your home dir!");
        }
    }
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct Config {
    pub consumer_key: String,
    pub consumer_secret: String,
    pub access_key: String,
    pub access_secret: String
}

impl Config {
    pub fn read(path_file : &Path) -> Option<Config> {
        let mut file = match File::open(path_file) {
            Ok(f) => f,
            Err(_) => return None
        };
        let conf = Json::from_reader(&mut file).unwrap();
        Decodable::decode(&mut json::Decoder::new(conf)).ok()
    }

    pub fn write(&self, path_file : &Path) {
        let mut file = match OpenOptions::new().write(true).open(path_file) {
            Ok(f) => f,
            Err(e) => panic!("{}", e)
        };
        let _ = write!(&mut file, "{}\n", &json::encode(self).unwrap());
    }
}

fn console_input(prompt: &str) -> String {
    print!("{}\n\t", prompt);
    let mut line = String::new();
    let _ = io::stdin().read_line(&mut line).unwrap();
    line.trim().to_string()
}

fn main() {
    let conf = match Config::read() {
        Some(c) => c,
        None => {
            let consumer_key    = console_input("input your consumer key:");
            let consumer_secret = console_input("input your consumer secret:");
            let consumer = Token::new(consumer_key, consumer_secret);

            let request = twitter::get_request_token(&consumer);
            println!("open the following url:");
            println!("\t{}", twitter::get_authorize_url(&request));
            let pin = console_input("input pin:");
            let access = twitter::get_access_token(&consumer, &request, &pin);

            let c = Config {
                consumer_key: consumer.key.to_string(),
                consumer_secret: consumer.secret.to_string(),
                access_key: access.key.to_string(),
                access_secret: access.secret.to_string()
            };
            c.write();
            c
        }
    };

    let consumer = Token::new(conf.consumer_key, conf.consumer_secret);
    let access = Token::new(conf.access_key, conf.access_secret);

    let status = console_input("What's happening?");
    twitter::tweet(&consumer, &access, &status);
}

<|endoftext|>


<commit_before>// This file is @generated by no_atomic.sh.
// It is not intended for manual editing.

const NO_ATOMIC_CAS: &[&str] = &[
    "avr-unknown-gnu-atmega328",
    "msp430-none-elf",
    "riscv32i-unknown-none-elf",
    "riscv32imc-unknown-none-elf",
    "thumbv4t-none-eabi",
    "thumbv6m-none-eabi",
];
#[allow(dead_code)]
const NO_ATOMIC_64: &[&str] = &[
    "arm-linux-androideabi",
    "armebv7r-none-eabi",
    "armebv7r-none-eabihf",
    "armv4t-unknown-linux-gnueabi",
    "armv5te-unknown-linux-gnueabi",
    "armv5te-unknown-linux-musleabi",
    "armv5te-unknown-linux-uclibceabi",
    "armv7r-none-eabi",
    "armv7r-none-eabihf",
    "hexagon-unknown-linux-musl",
    "mips-unknown-linux-gnu",
    "mips-unknown-linux-musl",
    "mips-unknown-linux-uclibc",
    "mipsel-unknown-linux-gnu",
    "mipsel-unknown-linux-musl",
    "mipsel-unknown-linux-uclibc",
    "mipsel-unknown-none",
    "mipsisa32r6-unknown-linux-gnu",
    "mipsisa32r6el-unknown-linux-gnu",
    "powerpc-unknown-linux-gnu",
    "powerpc-unknown-linux-gnuspe",
    "powerpc-unknown-linux-musl",
    "powerpc-unknown-netbsd",
    "powerpc-unknown-openbsd",
    "powerpc-wrs-vxworks",
    "powerpc-wrs-vxworks-spe",
    "riscv32gc-unknown-linux-gnu",
    "riscv32gc-unknown-linux-musl",
    "riscv32imac-unknown-none-elf",
    "thumbv7em-none-eabi",
    "thumbv7em-none-eabihf",
    "thumbv7m-none-eabi",
    "thumbv8m.base-none-eabi",
    "thumbv8m.main-none-eabi",
    "thumbv8m.main-none-eabihf",
    "mipsel-sony-psp",
    "thumbv4t-none-eabi",
    "thumbv6m-none-eabi",
];
#[allow(dead_code)]
const NO_ATOMIC: &[&str] = &[
    "avr-unknown-gnu-atmega328",
    "msp430-none-elf",
    "riscv32i-unknown-none-elf",
    "riscv32imc-unknown-none-elf",
];
<commit_msg>Update list of targets that do not support atomic CAS<commit_after>// This file is @generated by no_atomic.sh.
// It is not intended for manual editing.

const NO_ATOMIC_CAS: &[&str] = &[
    "avr-unknown-gnu-atmega328",
    "bpfeb-unknown-none",
    "bpfel-unknown-none",
    "msp430-none-elf",
    "riscv32i-unknown-none-elf",
    "riscv32imc-unknown-none-elf",
    "thumbv4t-none-eabi",
    "thumbv6m-none-eabi",
];
#[allow(dead_code)]
const NO_ATOMIC_64: &[&str] = &[
    "arm-linux-androideabi",
    "armebv7r-none-eabi",
    "armebv7r-none-eabihf",
    "armv4t-unknown-linux-gnueabi",
    "armv5te-unknown-linux-gnueabi",
    "armv5te-unknown-linux-musleabi",
    "armv5te-unknown-linux-uclibceabi",
    "armv7r-none-eabi",
    "armv7r-none-eabihf",
    "hexagon-unknown-linux-musl",
    "mips-unknown-linux-gnu",
    "mips-unknown-linux-musl",
    "mips-unknown-linux-uclibc",
    "mipsel-unknown-linux-gnu",
    "mipsel-unknown-linux-musl",
    "mipsel-unknown-linux-uclibc",
    "mipsel-unknown-none",
    "mipsisa32r6-unknown-linux-gnu",
    "mipsisa32r6el-unknown-linux-gnu",
    "powerpc-unknown-linux-gnu",
    "powerpc-unknown-linux-gnuspe",
    "powerpc-unknown-linux-musl",
    "powerpc-unknown-netbsd",
    "powerpc-unknown-openbsd",
    "powerpc-wrs-vxworks",
    "powerpc-wrs-vxworks-spe",
    "riscv32gc-unknown-linux-gnu",
    "riscv32gc-unknown-linux-musl",
    "riscv32imac-unknown-none-elf",
    "thumbv7em-none-eabi",
    "thumbv7em-none-eabihf",
    "thumbv7m-none-eabi",
    "thumbv8m.base-none-eabi",
    "thumbv8m.main-none-eabi",
    "thumbv8m.main-none-eabihf",
    "mipsel-sony-psp",
    "thumbv4t-none-eabi",
    "thumbv6m-none-eabi",
];
#[allow(dead_code)]
const NO_ATOMIC: &[&str] = &[
    "avr-unknown-gnu-atmega328",
    "msp430-none-elf",
    "riscv32i-unknown-none-elf",
    "riscv32imc-unknown-none-elf",
];
<|endoftext|>


<commit_before>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::collections::HashMap;
use std::sync::Arc;

use intern::string_key::StringKey;
use intern::string_key::StringKeyMap;
use schema::SDLSchema;

use crate::ir::ExecutableDefinition;
use crate::ir::FragmentDefinition;
use crate::ir::FragmentDefinitionName;
use crate::ir::FragmentDefinitionNameMap;
use crate::ir::OperationDefinition;
use crate::ir::OperationDefinitionName;

/// A collection of all documents that are being compiled.
#[derive(Debug, Clone)]
pub struct Program {
    pub schema: Arc<SDLSchema>,
    pub fragments: FragmentDefinitionNameMap<Arc<FragmentDefinition>>,
    pub operations: Vec<Arc<OperationDefinition>>,
}

impl Program {
    pub fn new(schema: Arc<SDLSchema>) -> Self {
        Self {
            schema,
            fragments: Default::default(),
            operations: Default::default(),
        }
    }

    pub fn from_definitions(
        schema: Arc<SDLSchema>,
        definitions: Vec<ExecutableDefinition>,
    ) -> Self {
        let mut operations = Vec::new();
        let mut fragments = HashMap::default();
        for definition in definitions {
            match definition {
                ExecutableDefinition::Operation(operation) => {
                    operations.push(Arc::new(operation));
                }
                ExecutableDefinition::Fragment(fragment) => {
                    fragments.insert(fragment.name.item, Arc::new(fragment));
                }
            }
        }
        Self {
            schema,
            fragments,
            operations,
        }
    }

    pub fn insert_fragment(&mut self, fragment: Arc<FragmentDefinition>) {
        let name = fragment.name.item;
        if let Some(previous) = self.fragments.insert(name, fragment) {
            panic!(
                "Can only insert '{}' once. Had {:?} and trying to insert {:?}.",
                name, previous, self.fragments[&name]
            );
        };
    }

    pub fn fragment(&self, name: FragmentDefinitionName) -> Option<&Arc<FragmentDefinition>> {
        self.fragments.get(&name)
    }

    pub fn fragment_mut(
        &mut self,
        name: FragmentDefinitionName,
    ) -> Option<&mut Arc<FragmentDefinition>> {
        self.fragments.get_mut(&name)
    }

    /// Searches for an operation by name.
    ///
    /// NOTE: This is a linear search, we currently don't frequently search
    ///       for operations by name, so this might be overall faster than
    ///       using a map internally.
    pub fn operation(&self, name: OperationDefinitionName) -> Option<&Arc<OperationDefinition>> {
        self.operations()
            .find(|operation| operation.name.item == name)
    }

    pub fn insert_operation(&mut self, operation: Arc<OperationDefinition>) {
        self.operations.push(operation);
    }

    pub fn operations(&self) -> impl Iterator<Item = &Arc<OperationDefinition>> {
        self.operations.iter()
    }

    pub fn fragments(&self) -> impl Iterator<Item = &Arc<FragmentDefinition>> {
        self.fragments.values()
    }

    pub fn document_count(&self) -> usize {
        self.fragments.len() + self.operations.len()
    }

    pub fn merge_program(
        &mut self,
        other_program: &Self,
        removed_definition_names: Option<&[StringKey]>,
    ) {
        let mut operations: StringKeyMap<Arc<OperationDefinition>> = self
            .operations
            .drain(..)
            .map(|op| (op.name.item.0, op))
            .collect();
        for fragment in other_program.fragments() {
            self.fragments
                .insert(fragment.name.item, Arc::clone(fragment));
        }
        for operation in other_program.operations() {
            operations.insert(operation.name.item.0, Arc::clone(operation));
        }
        if let Some(removed_definition_names) = removed_definition_names {
            for removed in removed_definition_names {
                self.fragments.remove(&FragmentDefinitionName(*removed));
                operations.remove(removed);
            }
        }
        self.operations
            .extend(operations.into_iter().map(|(_, op)| op));
    }
}
<commit_msg>Back out "Back out "[OSS] Disallow duplicate fragments when getting program""<commit_after>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::collections::HashMap;
use std::sync::Arc;

use intern::string_key::StringKey;
use intern::string_key::StringKeyMap;
use schema::SDLSchema;

use crate::ir::ExecutableDefinition;
use crate::ir::FragmentDefinition;
use crate::ir::FragmentDefinitionName;
use crate::ir::FragmentDefinitionNameMap;
use crate::ir::OperationDefinition;
use crate::ir::OperationDefinitionName;

/// A collection of all documents that are being compiled.
#[derive(Debug, Clone)]
pub struct Program {
    pub schema: Arc<SDLSchema>,
    pub fragments: FragmentDefinitionNameMap<Arc<FragmentDefinition>>,
    pub operations: Vec<Arc<OperationDefinition>>,
}

impl Program {
    pub fn new(schema: Arc<SDLSchema>) -> Self {
        Self {
            schema,
            fragments: Default::default(),
            operations: Default::default(),
        }
    }

    pub fn from_definitions(
        schema: Arc<SDLSchema>,
        definitions: Vec<ExecutableDefinition>,
    ) -> Self {
        let mut operations = Vec::new();
        let mut fragments = HashMap::default();
        let mut seen_operation_loc = HashMap::new();
        for definition in definitions {
            match definition {
                ExecutableDefinition::Operation(operation) => {
                    let loc = operation.name.location;
                    let name = operation.name.item;
                    if let Some(another) = seen_operation_loc.insert(name, loc) {
                        panic!(
                            "\nDuplicate operation definitions named {}: \nfirst one: {:?}\nsecond one: {:?}\n",
                            name, loc, another
                        );
                    }
                    operations.push(Arc::new(operation)); // Keep the order the operations same as inputs.
                }
                ExecutableDefinition::Fragment(fragment) => {
                    let loc = fragment.name.location;
                    let name = fragment.name.item;
                    if let Some(another) = fragments.insert(name, Arc::new(fragment)) {
                        panic!(
                            "\nDuplicate fragment definitions named {}: \nfirst one: {:?}\nsecond one: {:?}\n",
                            name, loc, &another.name.location
                        );
                    }
                }
            }
        }
        Self {
            schema,
            fragments,
            operations,
        }
    }

    pub fn insert_fragment(&mut self, fragment: Arc<FragmentDefinition>) {
        let name = fragment.name.item;
        if let Some(previous) = self.fragments.insert(name, fragment) {
            panic!(
                "Can only insert '{}' once. Had {:?} and trying to insert {:?}.",
                name, previous, self.fragments[&name]
            );
        };
    }

    pub fn fragment(&self, name: FragmentDefinitionName) -> Option<&Arc<FragmentDefinition>> {
        self.fragments.get(&name)
    }

    pub fn fragment_mut(
        &mut self,
        name: FragmentDefinitionName,
    ) -> Option<&mut Arc<FragmentDefinition>> {
        self.fragments.get_mut(&name)
    }

    /// Searches for an operation by name.
    ///
    /// NOTE: This is a linear search, we currently don't frequently search
    ///       for operations by name, so this might be overall faster than
    ///       using a map internally.
    pub fn operation(&self, name: OperationDefinitionName) -> Option<&Arc<OperationDefinition>> {
        self.operations()
            .find(|operation| operation.name.item == name)
    }

    pub fn insert_operation(&mut self, operation: Arc<OperationDefinition>) {
        self.operations.push(operation);
    }

    pub fn operations(&self) -> impl Iterator<Item = &Arc<OperationDefinition>> {
        self.operations.iter()
    }

    pub fn fragments(&self) -> impl Iterator<Item = &Arc<FragmentDefinition>> {
        self.fragments.values()
    }

    pub fn document_count(&self) -> usize {
        self.fragments.len() + self.operations.len()
    }

    pub fn merge_program(
        &mut self,
        other_program: &Self,
        removed_definition_names: Option<&[StringKey]>,
    ) {
        let mut operations: StringKeyMap<Arc<OperationDefinition>> = self
            .operations
            .drain(..)
            .map(|op| (op.name.item.0, op))
            .collect();
        for fragment in other_program.fragments() {
            self.fragments
                .insert(fragment.name.item, Arc::clone(fragment));
        }
        for operation in other_program.operations() {
            operations.insert(operation.name.item.0, Arc::clone(operation));
        }
        if let Some(removed_definition_names) = removed_definition_names {
            for removed in removed_definition_names {
                self.fragments.remove(&FragmentDefinitionName(*removed));
                operations.remove(removed);
            }
        }
        self.operations
            .extend(operations.into_iter().map(|(_, op)| op));
    }
}
<|endoftext|>


<commit_before>use serde_yaml::from_slice;
use std::collections::HashMap;

use data::types::*;
use data::components::*;
use data::{HasDependencies, dependencies_default};

static MODULE_LIST: &'static [u8] = include_bytes!("../../module_data.yml");

/// This struct is only for deserializing the included `module_data.yml`.
/// It shouldn't be used in any other context!
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Module {
    pub name: String,
    #[serde(default = "dependencies_default")]
    pub dependencies: Vec<(ResearchTypes, i32)>,
    pub levels: Vec<Level>,
}

impl HasDependencies for Module {
    fn get_dependencies(&self) -> &Vec<(ResearchTypes, i32)> {
        self.dependencies.as_ref()
    }
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Level {
    pub level: i32,
    pub resources: Option<Vec<(ResourceTypes, i64)>>,
    pub time: i64,
    pub shoots: Option<Shoots>,
    pub generates: Option<Vec<(ResourceTypes, i64)>>,
    pub consumes: Option<Vec<(ResourceTypes, i64)>>,
}

/// This function builds builds a HashMap from `module_data.yml`.
///
/// It contains: All modules, their levels, costs per level and research dependencies.
///
/// ```
/// static MODULE_LIST: &'static [u8] = include_bytes!("../../module_data.yml");
/// ```
///
/// # Panics
/// - If serde-yml tries to parse invalid yml.
pub fn get_module_list() -> HashMap<ModuleTypes, Module> {
    let result = from_slice::<HashMap<ModuleTypes, Module>>(MODULE_LIST);
    match result {
        Ok(v) => v,
        Err(e) => panic!("{:?}", e),
    }
}
<commit_msg>Set default empty list for generates and consumes in modules<commit_after>use serde_yaml::from_slice;
use std::collections::HashMap;

use data::types::*;
use data::components::*;
use data::{HasDependencies, dependencies_default};

static MODULE_LIST: &'static [u8] = include_bytes!("../../module_data.yml");

/// This struct is only for deserializing the included `module_data.yml`.
/// It shouldn't be used in any other context!
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Module {
    pub name: String,
    #[serde(default = "dependencies_default")]
    pub dependencies: Vec<(ResearchTypes, i32)>,
    pub levels: Vec<Level>,
}

impl HasDependencies for Module {
    fn get_dependencies(&self) -> &Vec<(ResearchTypes, i32)> {
        self.dependencies.as_ref()
    }
}

fn resource_default() -> Vec<(ResourceTypes, i64)> {Vec::new()}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Level {
    pub level: i32,
    pub resources: Option<Vec<(ResourceTypes, i64)>>,
    pub time: i64,
    pub shoots: Option<Shoots>,
    #[serde(default = "resource_default")]
    pub generates: Vec<(ResourceTypes, i64)>,
    #[serde(default = "resource_default")]
    pub consumes: Vec<(ResourceTypes, i64)>,
}

/// This function builds builds a HashMap from `module_data.yml`.
///
/// It contains: All modules, their levels, costs per level and research dependencies.
///
/// ```
/// static MODULE_LIST: &'static [u8] = include_bytes!("../../module_data.yml");
/// ```
///
/// # Panics
/// - If serde-yml tries to parse invalid yml.
pub fn get_module_list() -> HashMap<ModuleTypes, Module> {
    let result = from_slice::<HashMap<ModuleTypes, Module>>(MODULE_LIST);
    match result {
        Ok(v) => v,
        Err(e) => panic!("{:?}", e),
    }
}
<|endoftext|>


<commit_before>use wasm_bindgen::prelude::*;

use anyhow::Error;
use lancelot::{
    arch::Arch,
    aspace::AddressSpace,
    loader::pe::{PEError, PE as lPE},
    module::ModuleError,
    pagemap::PageMapError,
    util::UtilError,
};

#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

#[wasm_bindgen]
pub fn version() -> String {
    String::from("v0.7.0")
}

#[wasm_bindgen(start)]
pub fn main_js() -> Result<(), JsValue> {
    #[cfg(debug_assertions)]
    console_error_panic_hook::set_once();

    Ok(())
}

// for better function signatures
// no functional difference.
type JsError = JsValue;

fn to_value_error(e: anyhow::Error) -> JsError {
    js_sys::Error::new(&format!("{}", e)).into()
}

fn to_js_err(e: Error) -> JsError {
    match e.downcast_ref::<PEError>() {
        Some(PEError::FormatNotSupported(_)) => return to_value_error(e),
        Some(PEError::MalformedPEFile(_)) => return to_value_error(e),
        None => (),
    };

    #[allow(clippy::single_match)]
    match e.downcast_ref::<ModuleError>() {
        Some(ModuleError::InvalidAddress(_)) => return to_value_error(e),
        None => (),
    };

    match e.downcast_ref::<UtilError>() {
        Some(UtilError::FileAccess) => return to_value_error(e),
        Some(UtilError::FileFormat) => return to_value_error(e),
        None => (),
    };

    #[allow(clippy::single_match)]
    match e.downcast_ref::<PageMapError>() {
        Some(PageMapError::NotMapped) => return to_value_error(e),
        None => (),
    };

    to_value_error(e)
}

#[wasm_bindgen]
pub fn from_bytes(buf: Vec<u8>) -> Result<PE, JsError> {
    let pe = lPE::from_bytes(&buf).map_err(to_js_err)?;
    Ok(PE { inner: pe })
}

#[wasm_bindgen]
#[derive(Clone, Copy)]
pub struct AddressRange {
    pub start: u64,
    pub end:   u64,
}

#[wasm_bindgen]
impl AddressRange {
    #[wasm_bindgen(getter)]
    pub fn size(&self) -> u64 {
        self.end - self.start
    }
}

#[wasm_bindgen]
pub struct Section {
    pub physical_range: AddressRange,
    pub virtual_range:  AddressRange,
    // String are not clone, so we have to manually implement getters
    permissions:        String,
    name:               String,
}

#[wasm_bindgen]
impl Section {
    #[wasm_bindgen(getter)]
    pub fn permissions(&self) -> String {
        self.permissions.to_string()
    }

    #[wasm_bindgen(getter)]
    pub fn name(&self) -> String {
        self.name.clone()
    }
}

impl From<&lancelot::module::Section> for Section {
    fn from(other: &lancelot::module::Section) -> Section {
        Section {
            physical_range: AddressRange {
                start: other.physical_range.start,
                end:   other.physical_range.end,
            },
            virtual_range:  AddressRange {
                start: other.virtual_range.start,
                end:   other.virtual_range.end,
            },
            permissions:    other.permissions.to_string(),
            name:           other.name.to_string(),
        }
    }
}

#[wasm_bindgen]
pub struct PE {
    inner: lPE,
}

#[wasm_bindgen]
impl PE {
    #[wasm_bindgen(getter)]
    pub fn arch(&self) -> JsValue {
        match self.inner.module.arch {
            // we expect these strings may be returned many times,
            // so we intern them for perf
            // https://docs.rs/wasm-bindgen/0.2.78/wasm_bindgen/fn.intern.html
            Arch::X32 => JsValue::from(wasm_bindgen::intern("x32")),
            Arch::X64 => JsValue::from(wasm_bindgen::intern("x64")),
        }
    }

    #[wasm_bindgen(getter)]
    pub fn base_address(&self) -> u64 {
        self.inner.module.address_space.base_address
    }

    // not cheap, so its a function, not getter
    #[wasm_bindgen]
    pub fn functions(&self) -> Result<Vec<u64>, JsError> {
        Ok(lancelot::analysis::pe::find_functions(&self.inner)
            .map_err(to_js_err)?
            .into_iter()
            .filter(|f| matches!(f, lancelot::analysis::pe::Function::Local(_)))
            .map(|f| match f {
                lancelot::analysis::pe::Function::Local(va) => va,
                _ => unreachable!(),
            })
            .collect())
    }

    // Vec<T> cannot be serialized by wasm-bindgen
    // so we have to manually convert to Vec<JsValue>
    // and then annotate the TS type.
    //
    // NB: JS now owns the objects, must explicitly drop.
    #[wasm_bindgen(getter, typescript_type = "Array<Section>")]
    pub fn sections(&self) -> Vec<JsValue> {
        self.inner
            .module
            .sections
            .iter()
            .map(Section::from)
            .map(JsValue::from)
            .collect()
    }

    #[wasm_bindgen]
    pub fn read_bytes(&self, va: u64, size: usize) -> Result<Vec<u8>, JsError> {
        self.inner.module.address_space.read_bytes(va, size).map_err(to_js_err)
    }
}
<commit_msg>jslancelot: read_insn<commit_after>use wasm_bindgen::prelude::*;

use anyhow::Error;
use lancelot::{
    analysis::dis::{self, zydis},
    arch::Arch,
    aspace::AddressSpace,
    loader::pe::{PEError, PE as lPE},
    module::ModuleError,
    pagemap::PageMapError,
    util::UtilError,
};

#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

#[wasm_bindgen]
pub fn version() -> String {
    String::from("v0.7.0")
}

#[wasm_bindgen(start)]
pub fn main_js() -> Result<(), JsValue> {
    #[cfg(debug_assertions)]
    console_error_panic_hook::set_once();

    Ok(())
}

// for better function signatures
// no functional difference.
type JsError = JsValue;

fn to_value_error(e: anyhow::Error) -> JsError {
    js_sys::Error::new(&format!("{}", e)).into()
}

fn to_js_err(e: Error) -> JsError {
    match e.downcast_ref::<PEError>() {
        Some(PEError::FormatNotSupported(_)) => return to_value_error(e),
        Some(PEError::MalformedPEFile(_)) => return to_value_error(e),
        None => (),
    };

    #[allow(clippy::single_match)]
    match e.downcast_ref::<ModuleError>() {
        Some(ModuleError::InvalidAddress(_)) => return to_value_error(e),
        None => (),
    };

    match e.downcast_ref::<UtilError>() {
        Some(UtilError::FileAccess) => return to_value_error(e),
        Some(UtilError::FileFormat) => return to_value_error(e),
        None => (),
    };

    #[allow(clippy::single_match)]
    match e.downcast_ref::<PageMapError>() {
        Some(PageMapError::NotMapped) => return to_value_error(e),
        None => (),
    };

    to_value_error(e)
}

#[wasm_bindgen]
pub fn from_bytes(buf: Vec<u8>) -> Result<PE, JsError> {
    let pe = lPE::from_bytes(&buf).map_err(to_js_err)?;
    let dec = dis::get_disassembler(&pe.module).map_err(to_js_err)?;
    let formatter = zydis::Formatter::new(zydis::FormatterStyle::INTEL).expect("valid formatter options");
    Ok(PE {
        inner: pe,
        decoder: dec,
        formatter,
    })
}

#[wasm_bindgen]
#[derive(Clone, Copy)]
pub struct AddressRange {
    pub start: u64,
    pub end:   u64,
}

#[wasm_bindgen]
impl AddressRange {
    #[wasm_bindgen(getter)]
    pub fn size(&self) -> u64 {
        self.end - self.start
    }
}

#[wasm_bindgen]
pub struct Section {
    pub physical_range: AddressRange,
    pub virtual_range:  AddressRange,
    // String are not clone, so we have to manually implement getters
    permissions:        String,
    name:               String,
}

#[wasm_bindgen]
impl Section {
    #[wasm_bindgen(getter)]
    pub fn permissions(&self) -> String {
        self.permissions.to_string()
    }

    #[wasm_bindgen(getter)]
    pub fn name(&self) -> String {
        self.name.clone()
    }
}

impl From<&lancelot::module::Section> for Section {
    fn from(other: &lancelot::module::Section) -> Section {
        Section {
            physical_range: AddressRange {
                start: other.physical_range.start,
                end:   other.physical_range.end,
            },
            virtual_range:  AddressRange {
                start: other.virtual_range.start,
                end:   other.virtual_range.end,
            },
            permissions:    other.permissions.to_string(),
            name:           other.name.to_string(),
        }
    }
}

#[wasm_bindgen]
pub struct Instruction {
    pub address: u64,
    pub size:    u8,
    bytes:       Vec<u8>,
    string:      String,
}

#[wasm_bindgen]
impl Instruction {
    #[wasm_bindgen(getter)]
    pub fn bytes(&self) -> Vec<u8> {
        self.bytes.clone()
    }

    #[wasm_bindgen(getter)]
    pub fn string(&self) -> String {
        self.string.to_string()
    }
}

#[wasm_bindgen]
pub struct PE {
    inner:     lPE,
    decoder:   zydis::Decoder,
    formatter: zydis::Formatter,
}

#[wasm_bindgen]
impl PE {
    #[wasm_bindgen(getter)]
    pub fn arch(&self) -> JsValue {
        match self.inner.module.arch {
            // we expect these strings may be returned many times,
            // so we intern them for perf
            // https://docs.rs/wasm-bindgen/0.2.78/wasm_bindgen/fn.intern.html
            Arch::X32 => JsValue::from(wasm_bindgen::intern("x32")),
            Arch::X64 => JsValue::from(wasm_bindgen::intern("x64")),
        }
    }

    #[wasm_bindgen(getter)]
    pub fn base_address(&self) -> u64 {
        self.inner.module.address_space.base_address
    }

    // not cheap, so its a function, not getter
    #[wasm_bindgen]
    pub fn functions(&self) -> Result<Vec<u64>, JsError> {
        Ok(lancelot::analysis::pe::find_functions(&self.inner)
            .map_err(to_js_err)?
            .into_iter()
            .filter(|f| matches!(f, lancelot::analysis::pe::Function::Local(_)))
            .map(|f| match f {
                lancelot::analysis::pe::Function::Local(va) => va,
                _ => unreachable!(),
            })
            .collect())
    }

    // Vec<T> cannot be serialized by wasm-bindgen
    // so we have to manually convert to Vec<JsValue>
    // and then annotate the TS type.
    //
    // NB: JS now owns the objects, must explicitly drop.
    #[wasm_bindgen(getter, typescript_type = "Array<Section>")]
    pub fn sections(&self) -> Vec<JsValue> {
        self.inner
            .module
            .sections
            .iter()
            .map(Section::from)
            .map(JsValue::from)
            .collect()
    }

    #[wasm_bindgen]
    pub fn read_bytes(&self, va: u64, size: usize) -> Result<Vec<u8>, JsError> {
        self.inner.module.address_space.read_bytes(va, size).map_err(to_js_err)
    }

    #[wasm_bindgen]
    pub fn read_insn(&self, va: u64) -> Result<Instruction, JsError> {
        let mut insn_buf = [0u8; 16];
        self.inner
            .module
            .address_space
            .read_into(va, &mut insn_buf)
            .map_err(to_js_err)?;

        if let Ok(Some(insn)) = self.decoder.decode(&insn_buf) {
            let mut out_buf = [0u8; 200];
            let mut out_buf = zydis::OutputBuffer::new(&mut out_buf[..]);
            self.formatter
                .format_instruction(&insn, &mut out_buf, Some(va), None)
                .map_err(|e| -> JsError {
                    js_sys::Error::new(&format!("failed to format instruction: {}", e)).into()
                })?;

            Ok(Instruction {
                address: va,
                size:    insn.length,
                bytes:   insn_buf[..insn.length as usize].to_vec(),
                string:  out_buf
                    .as_str()
                    .expect("disassembly should always be utf-8")
                    .to_string(),
            })
        } else {
            Err(js_sys::Error::new("invalid instruction").into())
        }
    }
}
<|endoftext|>


<commit_before>// The example only works on Linux since Termion does not yet support
// Windows: https://gitlab.redox-os.org/redox-os/termion/-/issues/103
// The precise library doesn't matter much, so feel free to send a PR
// if there is a library with good Windows support.

fn main() -> Result<(), std::io::Error> {
    #[cfg(not(unix))]
    panic!("Sorry, this example currently only works on Unix!");

    #[cfg(unix)]
    unix_only::main()
}

#[cfg(unix)]
mod unix_only {
    use std::io::{self, Write};
    use termion::event::Key;
    use termion::input::TermRead;
    use termion::raw::{IntoRawMode, RawTerminal};
    use termion::screen::AlternateScreen;
    use termion::{color, cursor, style};
    use textwrap::{wrap, Options, WordSeparator, WordSplitter, WrapAlgorithm};

    #[cfg(feature = "hyphenation")]
    use hyphenation::{Language, Load, Standard};

    fn draw_margins(
        row: u16,
        col: u16,
        line_width: u16,
        left: char,
        right: char,
        stdout: &mut RawTerminal<io::Stdout>,
    ) -> Result<(), io::Error> {
        write!(
            stdout,
            "{}{}{}{}",
            cursor::Goto(col - 1, row),
            color::Fg(color::Red),
            left,
            color::Fg(color::Reset),
        )?;
        write!(
            stdout,
            "{}{}{}{}",
            cursor::Goto(col + line_width, row),
            color::Fg(color::Red),
            right,
            color::Fg(color::Reset),
        )?;

        Ok(())
    }

    fn draw_text<'a>(
        text: &str,
        options: &Options<'a>,
        word_splitter_label: &str,
        stdout: &mut RawTerminal<io::Stdout>,
    ) -> Result<(), io::Error> {
        let mut left_row: u16 = 1;
        let left_col: u16 = 3;

        write!(stdout, "{}", termion::clear::All)?;
        write!(
            stdout,
            "{}{}Options:{}",
            cursor::Goto(left_col, left_row),
            style::Bold,
            style::Reset,
        )?;
        left_row += 1;

        write!(
            stdout,
            "{}- width: {}{}{} (use ← and → to change)",
            cursor::Goto(left_col, left_row),
            style::Bold,
            options.width,
            style::Reset,
        )?;
        left_row += 1;

        write!(
            stdout,
            "{}- break_words: {}{:?}{} (toggle with Ctrl-b)",
            cursor::Goto(left_col, left_row),
            style::Bold,
            options.break_words,
            style::Reset,
        )?;
        left_row += 1;

        write!(
            stdout,
            "{}- splitter: {}{}{} (cycle with Ctrl-s)",
            cursor::Goto(left_col, left_row),
            style::Bold,
            word_splitter_label,
            style::Reset,
        )?;
        left_row += 1;

        #[cfg(feature = "smawk")]
        {
            // The OptimalFit struct formats itself with a ton of
            // parameters. This removes the parameters, leaving only
            // the struct name behind.
            let wrap_algorithm_label = format!("{:?}", options.wrap_algorithm)
                .split(' ')
                .next()
                .unwrap()
                .to_string();
            write!(
                stdout,
                "{}- algorithm: {}{}{} (toggle with Ctrl-o)",
                cursor::Goto(left_col, left_row),
                style::Bold,
                wrap_algorithm_label,
                style::Reset,
            )?;
            left_row += 1;
        }

        let now = std::time::Instant::now();
        let mut lines = wrap(text, options);
        let elapsed = now.elapsed();

        let right_col: u16 = 55;
        let mut right_row: u16 = 1;
        write!(
            stdout,
            "{}{}Performance:{}",
            cursor::Goto(right_col, right_row),
            style::Bold,
            style::Reset,
        )?;
        right_row += 1;

        write!(
            stdout,
            "{}- build: {}{}{}",
            cursor::Goto(right_col, right_row),
            style::Bold,
            if cfg!(debug_assertions) {
                "debug"
            } else {
                "release"
            },
            style::Reset,
        )?;
        right_row += 1;

        write!(
            stdout,
            "{}- words: {}{}{}",
            cursor::Goto(right_col, right_row),
            style::Bold,
            text.split_whitespace().count(),
            style::Reset,
        )?;
        right_row += 1;

        write!(
            stdout,
            "{}- characters: {}{}{}",
            cursor::Goto(right_col, right_row),
            style::Bold,
            text.chars().count(),
            style::Reset,
        )?;
        right_row += 1;

        write!(
            stdout,
            "{}- latency: {}{} usec{}",
            cursor::Goto(right_col, right_row),
            style::Bold,
            elapsed.as_micros(),
            style::Reset,
        )?;

        // Empty line.
        left_row += 1;

        if let Some(line) = lines.last_mut() {
            let trailing_whitespace = &text[text.trim_end_matches(' ').len()..];
            if !trailing_whitespace.is_empty() {
                // Trailing whitespace is discarded by
                // `textwrap::wrap`. We reinsert it here. If multiple
                // spaces are added, this can overflow the margins
                // which look a bit odd. Handling this would require
                // some more tinkering...
                *line = format!("{}{}", line, trailing_whitespace).into();
            } else if line.ends_with('\n') {
                // If `text` ends with a newline, the final wrapped line
                // contains this newline. This will in turn leave the
                // cursor hanging in the middle of the line. Pushing an
                // extra empty line fixes this.
                lines.push("".into());
            }
        } else {
            // No lines -> we add an empty line so we have a place
            // where we can display the cursor.
            lines.push("".into());
        }

        // Draw margins above and below the wrapped text. We draw the
        // margin before the text so that 1) the text can overwrite
        // the margin if `break_words` is `false` and `width` is very
        // small and 2) so the cursor remains at the end of the last
        // line of text.
        draw_margins(left_row, left_col, options.width as u16, '┌', '┐', stdout)?;
        left_row += 1;
        let final_row = left_row + lines.len() as u16;
        draw_margins(final_row, left_col, options.width as u16, '└', '┘', stdout)?;

        let (_, rows) = termion::terminal_size()?;
        write!(stdout, "{}", cursor::Show)?;
        for line in lines {
            if left_row > rows {
                // The text does not fits on the terminal -- we hide
                // the cursor since it's supposed to be "below" the
                // bottom of the terminal.
                write!(stdout, "{}", cursor::Hide)?;
                break;
            }
            draw_margins(left_row, left_col, options.width as u16, '│', '│', stdout)?;
            write!(stdout, "{}{}", cursor::Goto(left_col, left_row), line)?;
            left_row += 1;
        }

        stdout.flush()
    }

    pub fn main() -> Result<(), io::Error> {
        let mut wrap_algorithms = Vec::new();
        #[cfg(feature = "smawk")]
        wrap_algorithms.push(WrapAlgorithm::OptimalFit(
            textwrap::wrap_algorithms::Penalties::new(),
        ));
        wrap_algorithms.push(WrapAlgorithm::FirstFit);

        let mut word_splitters: Vec<WordSplitter> =
            vec![WordSplitter::HyphenSplitter, WordSplitter::NoHyphenation];
        let mut word_splitter_labels: Vec<String> =
            word_splitters.iter().map(|s| format!("{:?}", s)).collect();

        // If you like, you can download more dictionaries from
        // https://github.com/tapeinosyne/hyphenation/tree/master/dictionaries
        // Place the dictionaries in the examples/ directory. Here we
        // just load the embedded en-us dictionary.
        #[cfg(feature = "hyphenation")]
        for lang in &[Language::EnglishUS] {
            let dictionary = Standard::from_embedded(*lang).or_else(|_| {
                let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
                    .join("examples")
                    .join(format!("{}.standard.bincode", lang.code()));
                Standard::from_path(*lang, &path)
            });

            if let Ok(dict) = dictionary {
                word_splitters.insert(0, WordSplitter::Hyphenation(dict));
                word_splitter_labels.insert(0, format!("{} hyphenation", lang.code()));
            }
        }

        let mut options = Options::new(35)
            .break_words(false)
            .wrap_algorithm(wrap_algorithms.remove(0))
            .word_splitter(word_splitters.remove(0))
            .word_separator(WordSeparator::AsciiSpace);
        let mut word_splitter_label = word_splitter_labels.remove(0);

        let args = std::env::args().collect::<Vec<_>>();
        let mut text = if args.len() > 1 {
            args[1..].join(" ")
        } else {
            String::from(
                "Welcome to the interactive demo! The following is from The \
            Emperor’s New Clothes by Hans Christian Andersen. You can edit the \
            text!\n\n\
            Many years ago there was an Emperor, who was so excessively fond \
            of new clothes that he spent all his money on them. He cared \
            nothing about his soldiers, nor for the theatre, nor for driving \
            in the woods except for the sake of showing off his new clothes. \
            He had a costume for every hour in the day, and instead of saying, \
            as one does about any other king or emperor, ‘He is in his council \
            chamber,’ here one always said, ‘The Emperor is in his \
            dressing-room.’",
            )
        };

        let stdin = io::stdin();
        let mut screen = AlternateScreen::from(io::stdout().into_raw_mode()?);
        write!(screen, "{}", cursor::BlinkingUnderline)?;
        draw_text(&text, &options, &word_splitter_label, &mut screen)?;

        for c in stdin.keys() {
            match c? {
                Key::Esc | Key::Ctrl('c') => break,
                Key::Left => options.width = options.width.saturating_sub(1),
                Key::Right => options.width = options.width.saturating_add(1),
                Key::Ctrl('b') => options.break_words = !options.break_words,
                #[cfg(feature = "smawk")]
                Key::Ctrl('o') => {
                    std::mem::swap(&mut options.wrap_algorithm, &mut wrap_algorithms[0]);
                    wrap_algorithms.rotate_left(1);
                }
                Key::Ctrl('s') => {
                    // We always keep the next splitter at position 0.
                    std::mem::swap(&mut options.word_splitter, &mut word_splitters[0]);
                    word_splitters.rotate_left(1);
                    std::mem::swap(&mut word_splitter_label, &mut word_splitter_labels[0]);
                    word_splitter_labels.rotate_left(1);
                }
                Key::Char(c) => text.push(c),
                Key::Backspace => {
                    text.pop();
                }
                // Also known as Ctrl-Backspace
                Key::Ctrl('h') => text.truncate(text.rfind(' ').unwrap_or(0)),
                _ => {}
            }

            draw_text(&text, &options, &word_splitter_label, &mut screen)?;
        }

        // TODO: change to cursor::DefaultStyle if
        // https://github.com/redox-os/termion/pull/157 is merged.
        screen.write_all(b"\x1b[0 q")?;
        screen.flush()
    }
}
<commit_msg>Fix the display of `OptimalFit` in interactive demo<commit_after>// The example only works on Linux since Termion does not yet support
// Windows: https://gitlab.redox-os.org/redox-os/termion/-/issues/103
// The precise library doesn't matter much, so feel free to send a PR
// if there is a library with good Windows support.

fn main() -> Result<(), std::io::Error> {
    #[cfg(not(unix))]
    panic!("Sorry, this example currently only works on Unix!");

    #[cfg(unix)]
    unix_only::main()
}

#[cfg(unix)]
mod unix_only {
    use std::io::{self, Write};
    use termion::event::Key;
    use termion::input::TermRead;
    use termion::raw::{IntoRawMode, RawTerminal};
    use termion::screen::AlternateScreen;
    use termion::{color, cursor, style};
    use textwrap::{wrap, Options, WordSeparator, WordSplitter, WrapAlgorithm};

    #[cfg(feature = "hyphenation")]
    use hyphenation::{Language, Load, Standard};

    fn draw_margins(
        row: u16,
        col: u16,
        line_width: u16,
        left: char,
        right: char,
        stdout: &mut RawTerminal<io::Stdout>,
    ) -> Result<(), io::Error> {
        write!(
            stdout,
            "{}{}{}{}",
            cursor::Goto(col - 1, row),
            color::Fg(color::Red),
            left,
            color::Fg(color::Reset),
        )?;
        write!(
            stdout,
            "{}{}{}{}",
            cursor::Goto(col + line_width, row),
            color::Fg(color::Red),
            right,
            color::Fg(color::Reset),
        )?;

        Ok(())
    }

    fn draw_text<'a>(
        text: &str,
        options: &Options<'a>,
        word_splitter_label: &str,
        stdout: &mut RawTerminal<io::Stdout>,
    ) -> Result<(), io::Error> {
        let mut left_row: u16 = 1;
        let left_col: u16 = 3;

        write!(stdout, "{}", termion::clear::All)?;
        write!(
            stdout,
            "{}{}Options:{}",
            cursor::Goto(left_col, left_row),
            style::Bold,
            style::Reset,
        )?;
        left_row += 1;

        write!(
            stdout,
            "{}- width: {}{}{} (use ← and → to change)",
            cursor::Goto(left_col, left_row),
            style::Bold,
            options.width,
            style::Reset,
        )?;
        left_row += 1;

        write!(
            stdout,
            "{}- break_words: {}{:?}{} (toggle with Ctrl-b)",
            cursor::Goto(left_col, left_row),
            style::Bold,
            options.break_words,
            style::Reset,
        )?;
        left_row += 1;

        write!(
            stdout,
            "{}- splitter: {}{}{} (cycle with Ctrl-s)",
            cursor::Goto(left_col, left_row),
            style::Bold,
            word_splitter_label,
            style::Reset,
        )?;
        left_row += 1;

        #[cfg(feature = "smawk")]
        {
            // The OptimalFit struct formats itself with a ton of
            // parameters. This removes the parameters, leaving only
            // the struct name behind.
            let wrap_algorithm_label = format!("{:?}", options.wrap_algorithm)
                .split('(')
                .next()
                .unwrap()
                .to_string();
            write!(
                stdout,
                "{}- algorithm: {}{}{} (toggle with Ctrl-o)",
                cursor::Goto(left_col, left_row),
                style::Bold,
                wrap_algorithm_label,
                style::Reset,
            )?;
            left_row += 1;
        }

        let now = std::time::Instant::now();
        let mut lines = wrap(text, options);
        let elapsed = now.elapsed();

        let right_col: u16 = 55;
        let mut right_row: u16 = 1;
        write!(
            stdout,
            "{}{}Performance:{}",
            cursor::Goto(right_col, right_row),
            style::Bold,
            style::Reset,
        )?;
        right_row += 1;

        write!(
            stdout,
            "{}- build: {}{}{}",
            cursor::Goto(right_col, right_row),
            style::Bold,
            if cfg!(debug_assertions) {
                "debug"
            } else {
                "release"
            },
            style::Reset,
        )?;
        right_row += 1;

        write!(
            stdout,
            "{}- words: {}{}{}",
            cursor::Goto(right_col, right_row),
            style::Bold,
            text.split_whitespace().count(),
            style::Reset,
        )?;
        right_row += 1;

        write!(
            stdout,
            "{}- characters: {}{}{}",
            cursor::Goto(right_col, right_row),
            style::Bold,
            text.chars().count(),
            style::Reset,
        )?;
        right_row += 1;

        write!(
            stdout,
            "{}- latency: {}{} usec{}",
            cursor::Goto(right_col, right_row),
            style::Bold,
            elapsed.as_micros(),
            style::Reset,
        )?;

        // Empty line.
        left_row += 1;

        if let Some(line) = lines.last_mut() {
            let trailing_whitespace = &text[text.trim_end_matches(' ').len()..];
            if !trailing_whitespace.is_empty() {
                // Trailing whitespace is discarded by
                // `textwrap::wrap`. We reinsert it here. If multiple
                // spaces are added, this can overflow the margins
                // which look a bit odd. Handling this would require
                // some more tinkering...
                *line = format!("{}{}", line, trailing_whitespace).into();
            } else if line.ends_with('\n') {
                // If `text` ends with a newline, the final wrapped line
                // contains this newline. This will in turn leave the
                // cursor hanging in the middle of the line. Pushing an
                // extra empty line fixes this.
                lines.push("".into());
            }
        } else {
            // No lines -> we add an empty line so we have a place
            // where we can display the cursor.
            lines.push("".into());
        }

        // Draw margins above and below the wrapped text. We draw the
        // margin before the text so that 1) the text can overwrite
        // the margin if `break_words` is `false` and `width` is very
        // small and 2) so the cursor remains at the end of the last
        // line of text.
        draw_margins(left_row, left_col, options.width as u16, '┌', '┐', stdout)?;
        left_row += 1;
        let final_row = left_row + lines.len() as u16;
        draw_margins(final_row, left_col, options.width as u16, '└', '┘', stdout)?;

        let (_, rows) = termion::terminal_size()?;
        write!(stdout, "{}", cursor::Show)?;
        for line in lines {
            if left_row > rows {
                // The text does not fits on the terminal -- we hide
                // the cursor since it's supposed to be "below" the
                // bottom of the terminal.
                write!(stdout, "{}", cursor::Hide)?;
                break;
            }
            draw_margins(left_row, left_col, options.width as u16, '│', '│', stdout)?;
            write!(stdout, "{}{}", cursor::Goto(left_col, left_row), line)?;
            left_row += 1;
        }

        stdout.flush()
    }

    pub fn main() -> Result<(), io::Error> {
        let mut wrap_algorithms = Vec::new();
        #[cfg(feature = "smawk")]
        wrap_algorithms.push(WrapAlgorithm::OptimalFit(
            textwrap::wrap_algorithms::Penalties::new(),
        ));
        wrap_algorithms.push(WrapAlgorithm::FirstFit);

        let mut word_splitters: Vec<WordSplitter> =
            vec![WordSplitter::HyphenSplitter, WordSplitter::NoHyphenation];
        let mut word_splitter_labels: Vec<String> =
            word_splitters.iter().map(|s| format!("{:?}", s)).collect();

        // If you like, you can download more dictionaries from
        // https://github.com/tapeinosyne/hyphenation/tree/master/dictionaries
        // Place the dictionaries in the examples/ directory. Here we
        // just load the embedded en-us dictionary.
        #[cfg(feature = "hyphenation")]
        for lang in &[Language::EnglishUS] {
            let dictionary = Standard::from_embedded(*lang).or_else(|_| {
                let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
                    .join("examples")
                    .join(format!("{}.standard.bincode", lang.code()));
                Standard::from_path(*lang, &path)
            });

            if let Ok(dict) = dictionary {
                word_splitters.insert(0, WordSplitter::Hyphenation(dict));
                word_splitter_labels.insert(0, format!("{} hyphenation", lang.code()));
            }
        }

        let mut options = Options::new(35)
            .break_words(false)
            .wrap_algorithm(wrap_algorithms.remove(0))
            .word_splitter(word_splitters.remove(0))
            .word_separator(WordSeparator::AsciiSpace);
        let mut word_splitter_label = word_splitter_labels.remove(0);

        let args = std::env::args().collect::<Vec<_>>();
        let mut text = if args.len() > 1 {
            args[1..].join(" ")
        } else {
            String::from(
                "Welcome to the interactive demo! The following is from The \
            Emperor’s New Clothes by Hans Christian Andersen. You can edit the \
            text!\n\n\
            Many years ago there was an Emperor, who was so excessively fond \
            of new clothes that he spent all his money on them. He cared \
            nothing about his soldiers, nor for the theatre, nor for driving \
            in the woods except for the sake of showing off his new clothes. \
            He had a costume for every hour in the day, and instead of saying, \
            as one does about any other king or emperor, ‘He is in his council \
            chamber,’ here one always said, ‘The Emperor is in his \
            dressing-room.’",
            )
        };

        let stdin = io::stdin();
        let mut screen = AlternateScreen::from(io::stdout().into_raw_mode()?);
        write!(screen, "{}", cursor::BlinkingUnderline)?;
        draw_text(&text, &options, &word_splitter_label, &mut screen)?;

        for c in stdin.keys() {
            match c? {
                Key::Esc | Key::Ctrl('c') => break,
                Key::Left => options.width = options.width.saturating_sub(1),
                Key::Right => options.width = options.width.saturating_add(1),
                Key::Ctrl('b') => options.break_words = !options.break_words,
                #[cfg(feature = "smawk")]
                Key::Ctrl('o') => {
                    std::mem::swap(&mut options.wrap_algorithm, &mut wrap_algorithms[0]);
                    wrap_algorithms.rotate_left(1);
                }
                Key::Ctrl('s') => {
                    // We always keep the next splitter at position 0.
                    std::mem::swap(&mut options.word_splitter, &mut word_splitters[0]);
                    word_splitters.rotate_left(1);
                    std::mem::swap(&mut word_splitter_label, &mut word_splitter_labels[0]);
                    word_splitter_labels.rotate_left(1);
                }
                Key::Char(c) => text.push(c),
                Key::Backspace => {
                    text.pop();
                }
                // Also known as Ctrl-Backspace
                Key::Ctrl('h') => text.truncate(text.rfind(' ').unwrap_or(0)),
                _ => {}
            }

            draw_text(&text, &options, &word_splitter_label, &mut screen)?;
        }

        // TODO: change to cursor::DefaultStyle if
        // https://github.com/redox-os/termion/pull/157 is merged.
        screen.write_all(b"\x1b[0 q")?;
        screen.flush()
    }
}
<|endoftext|>