<commit_before>//! `cargo add`
#![warn(missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
        trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces,
        unused_qualifications)]

extern crate docopt;
#[macro_use]
extern crate error_chain;
extern crate semver;
#[macro_use]
extern crate serde_derive;
extern crate termcolor;

use std::process;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

extern crate cargo_edit;
use cargo_edit::{Dependency, Manifest};

mod args;
use args::Args;

mod errors {
    error_chain!{
        links {
            CargoEditLib(::cargo_edit::Error, ::cargo_edit::ErrorKind);
        }
    }
}
use errors::*;

static USAGE: &'static str = r#"
Usage:
    cargo add <crate> [--dev|--build|--optional] [--vers=<ver>|--git=<uri>|--path=<uri>] [options]
    cargo add <crates>... [--dev|--build|--optional] [options]
    cargo add (-h|--help)
    cargo add --version

Specify what crate to add:
    --vers <ver>            Specify the version to grab from the registry (crates.io).
                            You can also specify versions as part of the name, e.g
                            `cargo add bitflags@0.3.2`.
    --git <uri>             Specify a git repository to download the crate from.
    --path <uri>            Specify the path the crate should be loaded from.

Specify where to add the crate:
    -D --dev                Add crate as development dependency.
    -B --build              Add crate as build dependency.
    --optional              Add as an optional dependency (for use in features). This does not work
                            for `dev-dependencies` or `build-dependencies`.
    --target <target>       Add as dependency to the given target platform. This does not work
                            for `dev-dependencies` or `build-dependencies`.

Options:
    --upgrade=<method>      Choose method of semantic version upgrade. Must be one of
                            "none" (exact version), "patch" (`~` modifier), "minor"
                            (`^` modifier, default), or "all" (`>=`).
    --manifest-path=<path>  Path to the manifest to add a dependency to.
    --allow-prerelease      Include prerelease versions when fetching from crates.io (e.g.
                            '0.6.0-alpha'). Defaults to false.
    -q --quiet              Do not print any output in case of success.
    -h --help               Show this help page.
    -V --version            Show version.

This command allows you to add a dependency to a Cargo.toml manifest file. If <crate> is a github
or gitlab repository URL, or a local path, `cargo add` will try to automatically get the crate name
and set the appropriate `--git` or `--path` value.

Please note that Cargo treats versions like "1.2.3" as "^1.2.3" (and that "^1.2.3" is specified
as ">=1.2.3 and <2.0.0"). By default, `cargo add` will use this format, as it is the one that the
crates.io registry suggests. One goal of `cargo add` is to prevent you from using wildcard
dependencies (version set to "*").
"#;

fn print_msg(dep: &Dependency, section: &[String], optional: bool) {
    let optional = if optional { "optional " } else { "" }.to_owned();
    let section = if section.len() == 1 {
        section[0].clone()
    } else {
        "dependencies for target ".to_owned() + &section[1]
    };

    let mut output = StandardStream::stdout(ColorChoice::Auto);
    output
        .set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))
        .unwrap();
    print!("{:>12}", "Adding");
    output.reset().unwrap();
    println!(
        " {} v{} to {}{}",
        dep.name,
        dep.version().unwrap_or(""),
        optional,
        section
    );
}

fn handle_add(args: &Args) -> Result<()> {
    let manifest_path = args.flag_manifest_path.as_ref().map(From::from);
    let mut manifest = Manifest::open(&manifest_path)?;
    let deps = &args.parse_dependencies()?;

    deps.iter()
        .map(|dep| {
            if !args.flag_quiet {
                print_msg(dep, &args.get_section(), args.flag_optional);
            }
            manifest
                .insert_into_table(&args.get_section(), dep)
                .map_err(Into::into)
        })
        .collect::<Result<Vec<_>>>()
        .map_err(|err| {
            eprintln!("Could not edit `Cargo.toml`.\n\nERROR: {}", err);
            err
        })?;

    let mut file = Manifest::find_file(&manifest_path)?;
    manifest.write_to_file(&mut file)?;

    Ok(())
}

fn main() {
    let args = docopt::Docopt::new(USAGE)
        .and_then(|d| d.deserialize::<Args>())
        .unwrap_or_else(|err| err.exit());

    if args.flag_version {
        println!("cargo-add version {}", env!("CARGO_PKG_VERSION"));
        process::exit(0);
    }

    if let Err(err) = handle_add(&args) {
        eprintln!("Command failed due to unhandled error: {}\n", err);

        for e in err.iter().skip(1) {
            eprintln!("Caused by: {}", e);
        }

        if let Some(backtrace) = err.backtrace() {
            eprintln!("Backtrace: {:?}", backtrace);
        }

        process::exit(1);
    }
}
<commit_msg>Avoid allocations for string handling in `add/print_msg()`<commit_after>//! `cargo add`
#![warn(missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
        trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces,
        unused_qualifications)]

extern crate docopt;
#[macro_use]
extern crate error_chain;
extern crate semver;
#[macro_use]
extern crate serde_derive;
extern crate termcolor;

use std::process;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

extern crate cargo_edit;
use cargo_edit::{Dependency, Manifest};

mod args;
use args::Args;

mod errors {
    error_chain!{
        links {
            CargoEditLib(::cargo_edit::Error, ::cargo_edit::ErrorKind);
        }
    }
}
use errors::*;

static USAGE: &'static str = r#"
Usage:
    cargo add <crate> [--dev|--build|--optional] [--vers=<ver>|--git=<uri>|--path=<uri>] [options]
    cargo add <crates>... [--dev|--build|--optional] [options]
    cargo add (-h|--help)
    cargo add --version

Specify what crate to add:
    --vers <ver>            Specify the version to grab from the registry (crates.io).
                            You can also specify versions as part of the name, e.g
                            `cargo add bitflags@0.3.2`.
    --git <uri>             Specify a git repository to download the crate from.
    --path <uri>            Specify the path the crate should be loaded from.

Specify where to add the crate:
    -D --dev                Add crate as development dependency.
    -B --build              Add crate as build dependency.
    --optional              Add as an optional dependency (for use in features). This does not work
                            for `dev-dependencies` or `build-dependencies`.
    --target <target>       Add as dependency to the given target platform. This does not work
                            for `dev-dependencies` or `build-dependencies`.

Options:
    --upgrade=<method>      Choose method of semantic version upgrade. Must be one of
                            "none" (exact version), "patch" (`~` modifier), "minor"
                            (`^` modifier, default), or "all" (`>=`).
    --manifest-path=<path>  Path to the manifest to add a dependency to.
    --allow-prerelease      Include prerelease versions when fetching from crates.io (e.g.
                            '0.6.0-alpha'). Defaults to false.
    -q --quiet              Do not print any output in case of success.
    -h --help               Show this help page.
    -V --version            Show version.

This command allows you to add a dependency to a Cargo.toml manifest file. If <crate> is a github
or gitlab repository URL, or a local path, `cargo add` will try to automatically get the crate name
and set the appropriate `--git` or `--path` value.

Please note that Cargo treats versions like "1.2.3" as "^1.2.3" (and that "^1.2.3" is specified
as ">=1.2.3 and <2.0.0"). By default, `cargo add` will use this format, as it is the one that the
crates.io registry suggests. One goal of `cargo add` is to prevent you from using wildcard
dependencies (version set to "*").
"#;

fn print_msg(dep: &Dependency, section: &[String], optional: bool) {
    let optional = if optional { "optional " } else { "" };
    let section = if section.len() == 1 {
        section[0].clone()
    } else {
        format!("{} for target {}", &section[2], &section[1])
    };

    let mut output = StandardStream::stdout(ColorChoice::Auto);
    output
        .set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))
        .unwrap();
    print!("{:>12}", "Adding");
    output.reset().unwrap();
    println!(
        " {} v{} to {}{}",
        dep.name,
        dep.version().unwrap_or(""),
        optional,
        section
    );
}

fn handle_add(args: &Args) -> Result<()> {
    let manifest_path = args.flag_manifest_path.as_ref().map(From::from);
    let mut manifest = Manifest::open(&manifest_path)?;
    let deps = &args.parse_dependencies()?;

    deps.iter()
        .map(|dep| {
            if !args.flag_quiet {
                print_msg(dep, &args.get_section(), args.flag_optional);
            }
            manifest
                .insert_into_table(&args.get_section(), dep)
                .map_err(Into::into)
        })
        .collect::<Result<Vec<_>>>()
        .map_err(|err| {
            eprintln!("Could not edit `Cargo.toml`.\n\nERROR: {}", err);
            err
        })?;

    let mut file = Manifest::find_file(&manifest_path)?;
    manifest.write_to_file(&mut file)?;

    Ok(())
}

fn main() {
    let args = docopt::Docopt::new(USAGE)
        .and_then(|d| d.deserialize::<Args>())
        .unwrap_or_else(|err| err.exit());

    if args.flag_version {
        println!("cargo-add version {}", env!("CARGO_PKG_VERSION"));
        process::exit(0);
    }

    if let Err(err) = handle_add(&args) {
        eprintln!("Command failed due to unhandled error: {}\n", err);

        for e in err.iter().skip(1) {
            eprintln!("Caused by: {}", e);
        }

        if let Some(backtrace) = err.backtrace() {
            eprintln!("Backtrace: {:?}", backtrace);
        }

        process::exit(1);
    }
}
<|endoftext|>


<commit_before>// Copyright 2019 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use cursive::theme::{BaseColor, Color, ColorStyle};
use cursive::traits::Identifiable;
use cursive::view::View;
use cursive::views::BoxView;
use cursive::{Cursive, Printer};

use crate::tui::constants::VIEW_LOGS;
use cursive::utils::lines::spans::{LinesIterator, Row};
use cursive::utils::markup::StyledString;
use grin_util::logger::LogEntry;
use log::Level;
use std::collections::VecDeque;

pub struct TUILogsView;

impl TUILogsView {
	pub fn create() -> Box<dyn View> {
		let logs_view = BoxView::with_full_screen(LogBufferView::new(200).with_id("logs"));
		Box::new(logs_view.with_id(VIEW_LOGS))
	}

	pub fn update(c: &mut Cursive, entry: LogEntry) {
		c.call_on_id("logs", |t: &mut LogBufferView| {
			t.update(entry);
		});
	}
}

struct LogBufferView {
	buffer: VecDeque<LogEntry>,
}

impl LogBufferView {
	fn new(size: usize) -> Self {
		let mut buffer = VecDeque::new();
		buffer.resize(
			size,
			LogEntry {
				log: String::new(),
				level: Level::Info,
			},
		);

		LogBufferView { buffer }
	}

	fn update(&mut self, entry: LogEntry) {
		self.buffer.push_front(entry);
		self.buffer.pop_back();
	}

	fn color(level: Level) -> ColorStyle {
		match level {
			Level::Info => ColorStyle::new(
				Color::Light(BaseColor::Green),
				Color::Dark(BaseColor::Black),
			),
			Level::Warn => ColorStyle::new(
				Color::Light(BaseColor::Yellow),
				Color::Dark(BaseColor::Black),
			),
			Level::Error => {
				ColorStyle::new(Color::Light(BaseColor::Red), Color::Dark(BaseColor::Black))
			}
			_ => ColorStyle::new(
				Color::Light(BaseColor::White),
				Color::Dark(BaseColor::Black),
			),
		}
	}
}

impl View for LogBufferView {
	fn draw(&self, printer: &Printer) {
		let mut i = 0;
		for entry in self.buffer.iter().take(printer.size.y) {
			printer.with_color(LogBufferView::color(entry.level), |p| {
				let log_message = StyledString::plain(&entry.log);
				let mut rows: Vec<Row> = LinesIterator::new(&log_message, printer.size.x).collect();
				rows.reverse(); // So stack traces are in the right order.
				for row in rows {
					for span in row.resolve(&log_message) {
						p.print((0, p.size.y.saturating_sub(i + 1)), span.content);
						i += 1;
					}
				}
			});
		}
	}
}
<commit_msg>explicit str, rustc 1.34.0 doesn't seem to pick this up otherwise (#3191)<commit_after>// Copyright 2019 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use cursive::theme::{BaseColor, Color, ColorStyle};
use cursive::traits::Identifiable;
use cursive::view::View;
use cursive::views::BoxView;
use cursive::{Cursive, Printer};

use crate::tui::constants::VIEW_LOGS;
use cursive::utils::lines::spans::{LinesIterator, Row};
use cursive::utils::markup::StyledString;
use grin_util::logger::LogEntry;
use log::Level;
use std::collections::VecDeque;

pub struct TUILogsView;

impl TUILogsView {
	pub fn create() -> Box<dyn View> {
		let logs_view = BoxView::with_full_screen(LogBufferView::new(200).with_id("logs"));
		Box::new(logs_view.with_id(VIEW_LOGS))
	}

	pub fn update(c: &mut Cursive, entry: LogEntry) {
		c.call_on_id("logs", |t: &mut LogBufferView| {
			t.update(entry);
		});
	}
}

struct LogBufferView {
	buffer: VecDeque<LogEntry>,
}

impl LogBufferView {
	fn new(size: usize) -> Self {
		let mut buffer = VecDeque::new();
		buffer.resize(
			size,
			LogEntry {
				log: String::new(),
				level: Level::Info,
			},
		);

		LogBufferView { buffer }
	}

	fn update(&mut self, entry: LogEntry) {
		self.buffer.push_front(entry);
		self.buffer.pop_back();
	}

	fn color(level: Level) -> ColorStyle {
		match level {
			Level::Info => ColorStyle::new(
				Color::Light(BaseColor::Green),
				Color::Dark(BaseColor::Black),
			),
			Level::Warn => ColorStyle::new(
				Color::Light(BaseColor::Yellow),
				Color::Dark(BaseColor::Black),
			),
			Level::Error => {
				ColorStyle::new(Color::Light(BaseColor::Red), Color::Dark(BaseColor::Black))
			}
			_ => ColorStyle::new(
				Color::Light(BaseColor::White),
				Color::Dark(BaseColor::Black),
			),
		}
	}
}

impl View for LogBufferView {
	fn draw(&self, printer: &Printer) {
		let mut i = 0;
		for entry in self.buffer.iter().take(printer.size.y) {
			printer.with_color(LogBufferView::color(entry.level), |p| {
				let log_message = StyledString::plain(entry.log.as_str());
				let mut rows: Vec<Row> = LinesIterator::new(&log_message, printer.size.x).collect();
				rows.reverse(); // So stack traces are in the right order.
				for row in rows {
					for span in row.resolve(&log_message) {
						p.print((0, p.size.y.saturating_sub(i + 1)), span.content);
						i += 1;
					}
				}
			});
		}
	}
}
<|endoftext|>


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

use api::units::*;
use crate::internal_types::CacheTextureId;
use euclid::{point2, size2, default::Box2D};
use smallvec::SmallVec;

pub use etagere::AllocatorOptions as ShelfAllocatorOptions;
pub use etagere::BucketedAtlasAllocator as BucketedShelfAllocator;
pub use etagere::AtlasAllocator as ShelfAllocator;

/// ID of an allocation within a given allocator.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct AllocId(pub u32);

pub trait AtlasAllocator {
    /// Specific parameters of the allocator.
    type Parameters;
    /// Constructor
    fn new(size: i32, parameters: &Self::Parameters) -> Self;
    /// Allocate a rectangle.
    fn allocate(&mut self, size: DeviceIntSize) -> Option<(AllocId, DeviceIntRect)>;
    /// Deallocate a rectangle and return its size.
    fn deallocate(&mut self, id: AllocId);
    /// Return true if there is no live allocations.
    fn is_empty(&self) -> bool;
    /// Allocated area in pixels.
    fn allocated_space(&self) -> i32;
    /// Write a debug visualization of the atlas fitting in the provided rectangle.
    ///
    /// This is inserted in a larger dump so it shouldn't contain the xml start/end tags.
    fn dump_into_svg(&self, rect: &Box2D<f32>, output: &mut dyn std::io::Write) -> std::io::Result<()>;
}

pub trait AtlasAllocatorList<TextureParameters> {
    /// Allocate a rectangle.
    ///
    /// If allocation fails, call the provided callback, add a new allocator to the list and try again.
    fn allocate(
        &mut self,
        size: DeviceIntSize,
        texture_alloc_cb: &mut dyn FnMut(DeviceIntSize, &TextureParameters) -> CacheTextureId,
    ) -> (CacheTextureId, AllocId, DeviceIntRect);

    /// Deallocate a rectangle and return its size.
    fn deallocate(&mut self, texture_id: CacheTextureId, alloc_id: AllocId);

    fn texture_parameters(&self) -> &TextureParameters;
}

/// A number of 2D textures (single layer), each with a number of
/// regions that can act as a slab allocator.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
struct TextureUnit<Allocator> {
    allocator: Allocator,
    texture_id: CacheTextureId,
}

#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct AllocatorList<Allocator: AtlasAllocator, TextureParameters> {
    units: SmallVec<[TextureUnit<Allocator>; 1]>,
    size: i32,
    atlas_parameters: Allocator::Parameters,
    texture_parameters: TextureParameters,
}

impl<Allocator: AtlasAllocator, TextureParameters> AllocatorList<Allocator, TextureParameters> {
    pub fn new(
        size: i32,
        atlas_parameters: Allocator::Parameters,
        texture_parameters: TextureParameters,
    ) -> Self {
        AllocatorList {
            units: SmallVec::new(),
            size,
            atlas_parameters,
            texture_parameters,
        }
    }

    pub fn allocate(
        &mut self,
        requested_size: DeviceIntSize,
        texture_alloc_cb: &mut dyn FnMut(DeviceIntSize, &TextureParameters) -> CacheTextureId,
    ) -> (CacheTextureId, AllocId, DeviceIntRect) {
        // Try to allocate from one of the existing textures.
        for unit in &mut self.units {
            if let Some((alloc_id, rect)) = unit.allocator.allocate(requested_size) {
                return (unit.texture_id, alloc_id, rect);
            }
        }

        // Need to create a new texture to hold the allocation.
        let texture_id = texture_alloc_cb(size2(self.size, self.size), &self.texture_parameters);
        let unit_index = self.units.len();

        self.units.push(TextureUnit {
            allocator: Allocator::new(self.size, &self.atlas_parameters),
            texture_id,
        });

        let (alloc_id, rect) = self.units[unit_index]
            .allocator
            .allocate(requested_size)
            .unwrap();

        (texture_id, alloc_id, rect)
    }

    pub fn deallocate(&mut self, texture_id: CacheTextureId, alloc_id: AllocId) {
        let unit = self.units
            .iter_mut()
            .find(|unit| unit.texture_id == texture_id)
            .expect("Unable to find the associated texture array unit");

        unit.allocator.deallocate(alloc_id);
    }

    pub fn release_empty_textures<'l>(&mut self, texture_dealloc_cb: &'l mut dyn FnMut(CacheTextureId)) {
        self.units.retain(|unit| {
            if unit.allocator.is_empty() {
                texture_dealloc_cb(unit.texture_id);

                false
            } else{
                true
            }
        });
    }

    pub fn clear(&mut self, texture_dealloc_cb: &mut dyn FnMut(CacheTextureId)) {
        for unit in self.units.drain(..) {
            texture_dealloc_cb(unit.texture_id);
        }
    }

    #[allow(dead_code)]
    pub fn dump_as_svg(&self, output: &mut dyn std::io::Write) -> std::io::Result<()> {
        use svg_fmt::*;

        let num_arrays = self.units.len() as f32;

        let text_spacing = 15.0;
        let unit_spacing = 30.0;
        let texture_size = self.size as f32 / 2.0;

        let svg_w = unit_spacing * 2.0 + texture_size;
        let svg_h = unit_spacing + num_arrays * (texture_size + text_spacing + unit_spacing);

        writeln!(output, "{}", BeginSvg { w: svg_w, h: svg_h })?;

        // Background.
        writeln!(output,
            "    {}",
            rectangle(0.0, 0.0, svg_w, svg_h)
                .inflate(1.0, 1.0)
                .fill(rgb(50, 50, 50))
        )?;

        let mut y = unit_spacing;
        for unit in &self.units {
            writeln!(output, "    {}", text(unit_spacing, y, format!("{:?}", unit.texture_id)).color(rgb(230, 230, 230)))?;

            let rect = Box2D {
                min: point2(unit_spacing, y),
                max: point2(unit_spacing + texture_size, y + texture_size),
            };

            unit.allocator.dump_into_svg(&rect, output)?;

            y += unit_spacing + texture_size + text_spacing;
        }

        writeln!(output, "{}", EndSvg)
    }

    pub fn allocated_space(&self) -> i32 {
        let mut accum = 0;
        for unit in &self.units {
            accum += unit.allocator.allocated_space();
        }

        accum
    }

    pub fn allocated_textures(&self) -> usize {
        self.units.len()
    }
}

impl<Allocator: AtlasAllocator, TextureParameters> AtlasAllocatorList<TextureParameters>
for AllocatorList<Allocator, TextureParameters> {
    fn allocate(
        &mut self,
        requested_size: DeviceIntSize,
        texture_alloc_cb: &mut dyn FnMut(DeviceIntSize, &TextureParameters) -> CacheTextureId,
    ) -> (CacheTextureId, AllocId, DeviceIntRect) {
        self.allocate(requested_size, texture_alloc_cb)
    }

    fn deallocate(&mut self, texture_id: CacheTextureId, alloc_id: AllocId) {
        self.deallocate(texture_id, alloc_id);
    }

    fn texture_parameters(&self) -> &TextureParameters {
        &self.texture_parameters
    }
}

impl AtlasAllocator for BucketedShelfAllocator {
    type Parameters = ShelfAllocatorOptions;

    fn new(size: i32, options: &Self::Parameters) -> Self {
        BucketedShelfAllocator::with_options(size2(size, size), options)
    }

    fn allocate(&mut self, size: DeviceIntSize) -> Option<(AllocId, DeviceIntRect)> {
        self.allocate(size.to_untyped()).map(|alloc| {
            (AllocId(alloc.id.serialize()), alloc.rectangle.to_rect().cast_unit())
        })
    }

    fn deallocate(&mut self, id: AllocId) {
        self.deallocate(etagere::AllocId::deserialize(id.0));
    }

    fn is_empty(&self) -> bool {
        self.is_empty()
    }

    fn allocated_space(&self) -> i32 {
        self.allocated_space()
    }

    fn dump_into_svg(&self, rect: &Box2D<f32>, output: &mut dyn std::io::Write) -> std::io::Result<()> {
        self.dump_into_svg(Some(&rect.to_i32().cast_unit()), output)
    }
}

impl AtlasAllocator for ShelfAllocator {
    type Parameters = ShelfAllocatorOptions;

    fn new(size: i32, options: &Self::Parameters) -> Self {
        ShelfAllocator::with_options(size2(size, size), options)
    }

    fn allocate(&mut self, size: DeviceIntSize) -> Option<(AllocId, DeviceIntRect)> {
        self.allocate(size.to_untyped()).map(|alloc| {
            (AllocId(alloc.id.serialize()), alloc.rectangle.to_rect().cast_unit())
        })
    }

    fn deallocate(&mut self, id: AllocId) {
        self.deallocate(etagere::AllocId::deserialize(id.0));
    }

    fn is_empty(&self) -> bool {
        self.is_empty()
    }

    fn allocated_space(&self) -> i32 {
        self.allocated_space()
    }

    fn dump_into_svg(&self, rect: &Box2D<f32>, output: &mut dyn std::io::Write) -> std::io::Result<()> {
        self.dump_into_svg(Some(&rect.to_i32().cast_unit()), output)
    }
}
<commit_msg>Bug 1680769 - Add a test case. r=jnicol<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use api::units::*;
use crate::internal_types::CacheTextureId;
use euclid::{point2, size2, default::Box2D};
use smallvec::SmallVec;

pub use etagere::AllocatorOptions as ShelfAllocatorOptions;
pub use etagere::BucketedAtlasAllocator as BucketedShelfAllocator;
pub use etagere::AtlasAllocator as ShelfAllocator;

/// ID of an allocation within a given allocator.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct AllocId(pub u32);

pub trait AtlasAllocator {
    /// Specific parameters of the allocator.
    type Parameters;
    /// Constructor
    fn new(size: i32, parameters: &Self::Parameters) -> Self;
    /// Allocate a rectangle.
    fn allocate(&mut self, size: DeviceIntSize) -> Option<(AllocId, DeviceIntRect)>;
    /// Deallocate a rectangle and return its size.
    fn deallocate(&mut self, id: AllocId);
    /// Return true if there is no live allocations.
    fn is_empty(&self) -> bool;
    /// Allocated area in pixels.
    fn allocated_space(&self) -> i32;
    /// Write a debug visualization of the atlas fitting in the provided rectangle.
    ///
    /// This is inserted in a larger dump so it shouldn't contain the xml start/end tags.
    fn dump_into_svg(&self, rect: &Box2D<f32>, output: &mut dyn std::io::Write) -> std::io::Result<()>;
}

pub trait AtlasAllocatorList<TextureParameters> {
    /// Allocate a rectangle.
    ///
    /// If allocation fails, call the provided callback, add a new allocator to the list and try again.
    fn allocate(
        &mut self,
        size: DeviceIntSize,
        texture_alloc_cb: &mut dyn FnMut(DeviceIntSize, &TextureParameters) -> CacheTextureId,
    ) -> (CacheTextureId, AllocId, DeviceIntRect);

    /// Deallocate a rectangle and return its size.
    fn deallocate(&mut self, texture_id: CacheTextureId, alloc_id: AllocId);

    fn texture_parameters(&self) -> &TextureParameters;
}

/// A number of 2D textures (single layer), each with a number of
/// regions that can act as a slab allocator.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
struct TextureUnit<Allocator> {
    allocator: Allocator,
    texture_id: CacheTextureId,
}

#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct AllocatorList<Allocator: AtlasAllocator, TextureParameters> {
    units: SmallVec<[TextureUnit<Allocator>; 1]>,
    size: i32,
    atlas_parameters: Allocator::Parameters,
    texture_parameters: TextureParameters,
}

impl<Allocator: AtlasAllocator, TextureParameters> AllocatorList<Allocator, TextureParameters> {
    pub fn new(
        size: i32,
        atlas_parameters: Allocator::Parameters,
        texture_parameters: TextureParameters,
    ) -> Self {
        AllocatorList {
            units: SmallVec::new(),
            size,
            atlas_parameters,
            texture_parameters,
        }
    }

    pub fn allocate(
        &mut self,
        requested_size: DeviceIntSize,
        texture_alloc_cb: &mut dyn FnMut(DeviceIntSize, &TextureParameters) -> CacheTextureId,
    ) -> (CacheTextureId, AllocId, DeviceIntRect) {
        // Try to allocate from one of the existing textures.
        for unit in &mut self.units {
            if let Some((alloc_id, rect)) = unit.allocator.allocate(requested_size) {
                return (unit.texture_id, alloc_id, rect);
            }
        }

        // Need to create a new texture to hold the allocation.
        let texture_id = texture_alloc_cb(size2(self.size, self.size), &self.texture_parameters);
        let unit_index = self.units.len();

        self.units.push(TextureUnit {
            allocator: Allocator::new(self.size, &self.atlas_parameters),
            texture_id,
        });

        let (alloc_id, rect) = self.units[unit_index]
            .allocator
            .allocate(requested_size)
            .unwrap();

        (texture_id, alloc_id, rect)
    }

    pub fn deallocate(&mut self, texture_id: CacheTextureId, alloc_id: AllocId) {
        let unit = self.units
            .iter_mut()
            .find(|unit| unit.texture_id == texture_id)
            .expect("Unable to find the associated texture array unit");

        unit.allocator.deallocate(alloc_id);
    }

    pub fn release_empty_textures<'l>(&mut self, texture_dealloc_cb: &'l mut dyn FnMut(CacheTextureId)) {
        self.units.retain(|unit| {
            if unit.allocator.is_empty() {
                texture_dealloc_cb(unit.texture_id);

                false
            } else{
                true
            }
        });
    }

    pub fn clear(&mut self, texture_dealloc_cb: &mut dyn FnMut(CacheTextureId)) {
        for unit in self.units.drain(..) {
            texture_dealloc_cb(unit.texture_id);
        }
    }

    #[allow(dead_code)]
    pub fn dump_as_svg(&self, output: &mut dyn std::io::Write) -> std::io::Result<()> {
        use svg_fmt::*;

        let num_arrays = self.units.len() as f32;

        let text_spacing = 15.0;
        let unit_spacing = 30.0;
        let texture_size = self.size as f32 / 2.0;

        let svg_w = unit_spacing * 2.0 + texture_size;
        let svg_h = unit_spacing + num_arrays * (texture_size + text_spacing + unit_spacing);

        writeln!(output, "{}", BeginSvg { w: svg_w, h: svg_h })?;

        // Background.
        writeln!(output,
            "    {}",
            rectangle(0.0, 0.0, svg_w, svg_h)
                .inflate(1.0, 1.0)
                .fill(rgb(50, 50, 50))
        )?;

        let mut y = unit_spacing;
        for unit in &self.units {
            writeln!(output, "    {}", text(unit_spacing, y, format!("{:?}", unit.texture_id)).color(rgb(230, 230, 230)))?;

            let rect = Box2D {
                min: point2(unit_spacing, y),
                max: point2(unit_spacing + texture_size, y + texture_size),
            };

            unit.allocator.dump_into_svg(&rect, output)?;

            y += unit_spacing + texture_size + text_spacing;
        }

        writeln!(output, "{}", EndSvg)
    }

    pub fn allocated_space(&self) -> i32 {
        let mut accum = 0;
        for unit in &self.units {
            accum += unit.allocator.allocated_space();
        }

        accum
    }

    pub fn allocated_textures(&self) -> usize {
        self.units.len()
    }
}

impl<Allocator: AtlasAllocator, TextureParameters> AtlasAllocatorList<TextureParameters>
for AllocatorList<Allocator, TextureParameters> {
    fn allocate(
        &mut self,
        requested_size: DeviceIntSize,
        texture_alloc_cb: &mut dyn FnMut(DeviceIntSize, &TextureParameters) -> CacheTextureId,
    ) -> (CacheTextureId, AllocId, DeviceIntRect) {
        self.allocate(requested_size, texture_alloc_cb)
    }

    fn deallocate(&mut self, texture_id: CacheTextureId, alloc_id: AllocId) {
        self.deallocate(texture_id, alloc_id);
    }

    fn texture_parameters(&self) -> &TextureParameters {
        &self.texture_parameters
    }
}

impl AtlasAllocator for BucketedShelfAllocator {
    type Parameters = ShelfAllocatorOptions;

    fn new(size: i32, options: &Self::Parameters) -> Self {
        BucketedShelfAllocator::with_options(size2(size, size), options)
    }

    fn allocate(&mut self, size: DeviceIntSize) -> Option<(AllocId, DeviceIntRect)> {
        self.allocate(size.to_untyped()).map(|alloc| {
            (AllocId(alloc.id.serialize()), alloc.rectangle.to_rect().cast_unit())
        })
    }

    fn deallocate(&mut self, id: AllocId) {
        self.deallocate(etagere::AllocId::deserialize(id.0));
    }

    fn is_empty(&self) -> bool {
        self.is_empty()
    }

    fn allocated_space(&self) -> i32 {
        self.allocated_space()
    }

    fn dump_into_svg(&self, rect: &Box2D<f32>, output: &mut dyn std::io::Write) -> std::io::Result<()> {
        self.dump_into_svg(Some(&rect.to_i32().cast_unit()), output)
    }
}

impl AtlasAllocator for ShelfAllocator {
    type Parameters = ShelfAllocatorOptions;

    fn new(size: i32, options: &Self::Parameters) -> Self {
        ShelfAllocator::with_options(size2(size, size), options)
    }

    fn allocate(&mut self, size: DeviceIntSize) -> Option<(AllocId, DeviceIntRect)> {
        self.allocate(size.to_untyped()).map(|alloc| {
            (AllocId(alloc.id.serialize()), alloc.rectangle.to_rect().cast_unit())
        })
    }

    fn deallocate(&mut self, id: AllocId) {
        self.deallocate(etagere::AllocId::deserialize(id.0));
    }

    fn is_empty(&self) -> bool {
        self.is_empty()
    }

    fn allocated_space(&self) -> i32 {
        self.allocated_space()
    }

    fn dump_into_svg(&self, rect: &Box2D<f32>, output: &mut dyn std::io::Write) -> std::io::Result<()> {
        self.dump_into_svg(Some(&rect.to_i32().cast_unit()), output)
    }
}

#[test]
fn bug_1680769() {
    let mut allocators: AllocatorList<ShelfAllocator, ()> = AllocatorList::new(
        1024,
        ShelfAllocatorOptions::default(),
        (),
    );

    let mut allocations = Vec::new();
    let mut next_id = CacheTextureId(0);
    let alloc_cb = &mut |_: DeviceIntSize, _: &()| {
        let texture_id = next_id;
        next_id.0 += 1;

        texture_id
    };

    // Make some allocations, forcing the the creation of multiple textures.
    for _ in 0..50 {
        allocations.push(allocators.allocate(size2(256, 256), alloc_cb));
    }

    // Deallocate everything.
    // It should empty all atlases and we still have textures allocated because
    // we haven't called release_empty_textures yet.
    for alloc in allocations.drain(..) {
        allocators.deallocate(alloc.0, alloc.1);
    }

    // Allocate something else.
    // Bug 1680769 was causing this allocation to be duplicated and leaked in
    // all textures.
    allocations.push(allocators.allocate(size2(8, 8), alloc_cb));

    // Deallocate all known allocations.
    for alloc in allocations.drain(..) {
        allocators.deallocate(alloc.0, alloc.1);
    }

    // If we have leaked items, this won't manage to remove all textures.
    allocators.release_empty_textures(&mut |_| {});

    assert_eq!(allocators.allocated_textures(), 0);
}
<|endoftext|>


<commit_before>use nix::unistd::*;
use nix::unistd::ForkResult::*;
use nix::sys::wait::*;
use std::ffi::CString;

#[test]
fn test_fork_and_waitpid() {
    let pid = fork();
    match pid {
      Ok(Child) => {} // ignore child here
      Ok(Parent { child }) => {
          // assert that child was created and pid > 0
          assert!(child > 0);
          let wait_status = waitpid(child, None);
          match wait_status {
              // assert that waitpid returned correct status and the pid is the one of the child
              Ok(WaitStatus::Exited(pid_t, _)) =>  assert!(pid_t == child),

              // panic, must never happen
              Ok(_) => panic!("Child still alive, should never happen"),

              // panic, waitpid should never fail
              Err(_) => panic!("Error: waitpid Failed")
          }

      },
      // panic, fork should never fail unless there is a serious problem with the OS
      Err(_) => panic!("Error: Fork Failed")
    }
}

#[test]
fn test_wait() {
    let pid = fork();
    match pid {
      Ok(Child) => {} // ignore child here
      Ok(Parent { child }) => {
          let wait_status = wait();

          // just assert that (any) one child returns with WaitStatus::Exited
          assert_eq!(wait_status, Ok(WaitStatus::Exited(child, 0)));
      },
      // panic, fork should never fail unless there is a serious problem with the OS
      Err(_) => panic!("Error: Fork Failed")
    }
}

#[test]
fn test_mkstemp() {
    let result = mkstemp("/tmp/tempfile.XXXXXXXX");
    match result {
        Ok(fd) => {
            close(fd).expect("Couldn't close the file descriptor");
        }
        Err(e) => panic!("mkstemp failed: {}", e)
    }
}

#[test]
fn test_getpid() {
    let pid = getpid();
    let ppid = getppid();
    assert!(pid > 0);
    assert!(ppid > 0);
}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
    use nix::unistd::gettid;

    #[test]
    fn test_gettid() {
        let tid = gettid();
        assert!(tid > 0);
    }
}

macro_rules! execve_test_factory(
    ($test_name:ident, $syscall:ident, $unix_sh:expr, $android_sh:expr) => (
    #[test]
    fn $test_name() {
        // The `exec`d process will write to `writer`, and we'll read that
        // data from `reader`.
        let (reader, writer) = pipe().unwrap();

        match fork().unwrap() {
            Child => {
                #[cfg(not(target_os = "android"))]
                const SH_PATH: &'static [u8] = $unix_sh;

                #[cfg(target_os = "android")]
                const SH_PATH: &'static [u8] = $android_sh;

                // Close stdout.
                close(1).unwrap();
                // Make `writer` be the stdout of the new process.
                dup(writer).unwrap();
                // exec!
                $syscall(
                    &CString::new(SH_PATH).unwrap(),
                    &[CString::new(b"".as_ref()).unwrap(),
                      CString::new(b"-c".as_ref()).unwrap(),
                      CString::new(b"echo nix!!! && echo foo=$foo && echo baz=$baz"
                                   .as_ref()).unwrap()],
                    &[CString::new(b"foo=bar".as_ref()).unwrap(),
                      CString::new(b"baz=quux".as_ref()).unwrap()]).unwrap();
            },
            Parent { child } => {
                // Wait for the child to exit.
                waitpid(child, None).unwrap();
                // Read 1024 bytes.
                let mut buf = [0u8; 1024];
                read(reader, &mut buf).unwrap();
                // It should contain the things we printed using `/bin/sh`.
                let string = String::from_utf8_lossy(&buf);
                assert!(string.contains("nix!!!"));
                assert!(string.contains("foo=bar"));
                assert!(string.contains("baz=quux"));
            }
        }
    }
    )
);

execve_test_factory!(test_execve, execve, b"/bin/sh", b"/system/bin/sh");

#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(feature = "execvpe")]
execve_test_factory!(test_execvpe, execvpe, b"sh", b"sh");
<commit_msg>Remove dependency on Result::expect<commit_after>use nix::unistd::*;
use nix::unistd::ForkResult::*;
use nix::sys::wait::*;
use std::ffi::CString;

#[test]
fn test_fork_and_waitpid() {
    let pid = fork();
    match pid {
      Ok(Child) => {} // ignore child here
      Ok(Parent { child }) => {
          // assert that child was created and pid > 0
          assert!(child > 0);
          let wait_status = waitpid(child, None);
          match wait_status {
              // assert that waitpid returned correct status and the pid is the one of the child
              Ok(WaitStatus::Exited(pid_t, _)) =>  assert!(pid_t == child),

              // panic, must never happen
              Ok(_) => panic!("Child still alive, should never happen"),

              // panic, waitpid should never fail
              Err(_) => panic!("Error: waitpid Failed")
          }

      },
      // panic, fork should never fail unless there is a serious problem with the OS
      Err(_) => panic!("Error: Fork Failed")
    }
}

#[test]
fn test_wait() {
    let pid = fork();
    match pid {
      Ok(Child) => {} // ignore child here
      Ok(Parent { child }) => {
          let wait_status = wait();

          // just assert that (any) one child returns with WaitStatus::Exited
          assert_eq!(wait_status, Ok(WaitStatus::Exited(child, 0)));
      },
      // panic, fork should never fail unless there is a serious problem with the OS
      Err(_) => panic!("Error: Fork Failed")
    }
}

#[test]
fn test_mkstemp() {
    let result = mkstemp("/tmp/tempfile.XXXXXXXX");
    match result {
        Ok(fd) => {
            close(fd).unwrap();
        }
        Err(e) => panic!("mkstemp failed: {}", e)
    }
}

#[test]
fn test_getpid() {
    let pid = getpid();
    let ppid = getppid();
    assert!(pid > 0);
    assert!(ppid > 0);
}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
    use nix::unistd::gettid;

    #[test]
    fn test_gettid() {
        let tid = gettid();
        assert!(tid > 0);
    }
}

macro_rules! execve_test_factory(
    ($test_name:ident, $syscall:ident, $unix_sh:expr, $android_sh:expr) => (
    #[test]
    fn $test_name() {
        // The `exec`d process will write to `writer`, and we'll read that
        // data from `reader`.
        let (reader, writer) = pipe().unwrap();

        match fork().unwrap() {
            Child => {
                #[cfg(not(target_os = "android"))]
                const SH_PATH: &'static [u8] = $unix_sh;

                #[cfg(target_os = "android")]
                const SH_PATH: &'static [u8] = $android_sh;

                // Close stdout.
                close(1).unwrap();
                // Make `writer` be the stdout of the new process.
                dup(writer).unwrap();
                // exec!
                $syscall(
                    &CString::new(SH_PATH).unwrap(),
                    &[CString::new(b"".as_ref()).unwrap(),
                      CString::new(b"-c".as_ref()).unwrap(),
                      CString::new(b"echo nix!!! && echo foo=$foo && echo baz=$baz"
                                   .as_ref()).unwrap()],
                    &[CString::new(b"foo=bar".as_ref()).unwrap(),
                      CString::new(b"baz=quux".as_ref()).unwrap()]).unwrap();
            },
            Parent { child } => {
                // Wait for the child to exit.
                waitpid(child, None).unwrap();
                // Read 1024 bytes.
                let mut buf = [0u8; 1024];
                read(reader, &mut buf).unwrap();
                // It should contain the things we printed using `/bin/sh`.
                let string = String::from_utf8_lossy(&buf);
                assert!(string.contains("nix!!!"));
                assert!(string.contains("foo=bar"));
                assert!(string.contains("baz=quux"));
            }
        }
    }
    )
);

execve_test_factory!(test_execve, execve, b"/bin/sh", b"/system/bin/sh");

#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(feature = "execvpe")]
execve_test_factory!(test_execvpe, execvpe, b"sh", b"sh");
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(clippy)]

extern crate irc as irclib;
extern crate evalbotlib as backend;
extern crate rustc_serialize;

use std::thread;
use std::time::Duration;
use std::sync::mpsc;
use std::collections::HashMap;

use backend::{EvalSvc, EvalSvcCfg, Response, util};

use self::irclib::client::server::NetIrcServer;
use self::irclib::client::prelude as ircp;
use self::irclib::client::prelude::{Server, ServerExt};

macro_rules! send_msg {
    ($conn:expr, $dest:expr, $line:expr) => {
        if let Err(x) = $conn.send_privmsg($dest, $line) {
            println!("failed to send message: {}", x)
        }
    };
}

macro_rules! send_notice {
    ($conn:expr, $dest:expr, $line:expr) => {
        if let Err(x) = $conn.send_notice($dest, $line) {
            println!("failed to send notice: {}", x)
        }
    };
}

type EvalWorkerSender = mpsc::Sender<(IrcMessage, SendMsgFnBox)>;
type EvalWorkerReceiver = mpsc::Receiver<(IrcMessage, SendMsgFnBox)>;
type SendMsgFnBox = Box<Fn(&str, &str) + Send>;

#[derive(Clone, RustcDecodable, Default, PartialEq, Debug)]
struct IrcCfg {
    output: OutputCfg,
    command_prefix: String,
    owners: Vec<String>,
    irc_networks: Vec<ircp::Config>
}

#[derive(Clone, RustcDecodable, Default, PartialEq, Debug)]
struct OutputCfg {
    chan_output_prefix: String,
    max_channel_lines: usize,
    max_channel_line_len: usize,
    max_priv_lines: usize
}

fn main() {
    let config = match util::decode::<IrcCfg>("evalbot.irc.toml") {
        Ok(x) => x,
        Err(x) => {
            println!("failed to read evalbot.irc.toml: {:?}", x);
            return;
        }
    };
    let (tx, rx) = mpsc::channel();

    {
        let svc = match start_evalsvc() {
            Ok(x) => x,
            Err(()) => return,
        };
        let ocfg = config.output.clone();
        thread::spawn(move || eval_worker(svc, rx, ocfg));
    }

    let (qtx, qrx) = mpsc::channel();

    for (k, nwk) in config.irc_networks
                          .iter()
                          .filter_map(|cfg| {
                              match ircp::IrcServer::from_config(cfg.clone()) {
                                  Ok(conn) => Some(conn),
                                  Err(ref err) => {
                                      println!("failed to create IRC connection from Config {:#?}: {:?}", cfg, err);
                                      None
                                  }
                              }
                          })
                          .enumerate() {
        start_worker(nwk,
                     tx.clone(),
                     config.owners.clone(),
                     config.command_prefix.clone(),
                     qtx.clone(),
                     format!("{}", k));
    }
    util::ignore(qrx.recv());
}

fn start_evalsvc() -> Result<EvalSvc, ()> {
    let evalcfg = match util::decode::<EvalSvcCfg>("evalbot.toml") {
        Ok(x) => x,
        Err(x) => {
            println!("failed to read evalbot.toml: {:?}", x);
            return Err(());
        }
    };
    Ok(EvalSvc::new(evalcfg))
}

fn start_worker(conn: NetIrcServer,
    svc: EvalWorkerSender,
    owners: Vec<String>,
    cmd_prefix: String,
    quit_handle: mpsc::Sender<()>,
    conn_hash: String)
                -> thread::JoinHandle<()> {
    thread::spawn(move || {
        worker(conn, svc, owners, cmd_prefix, quit_handle, conn_hash);
    })
}

#[derive(Clone, Debug, Hash, PartialEq, Eq)]
struct MessageSource {
    chan: Option<String>,
    sender: String,
    conn_hash: String
}

impl MessageSource {
    fn reply_to(&self) -> &str {
        if let Some(ref chan) = self.chan { chan } else { &self.sender }
    }

    fn is_chan(&self) -> bool {
        self.chan.is_some()
    }
}

#[derive(Debug)]
struct IrcMessage {
    sender: MessageSource,
    data: MessageData
}

#[derive(Debug)]
enum MessageData {
    EvalReq {
        lang: String,
        code: String,
        timeout: bool
    },
    Quit,
    Rehash,
    Restart {
        lang: String
    },
    Raw {
        msg: String
    },
    Multiline {
        lang: String,
        code: String
    },
    CancelMultiline {
        lang: String
    }
}

unsafe impl Sync for IrcMessage {}

fn parse_msg(conn_hash: &str, message: &ircp::Message, owners: &[String], cmd_prefix: &str) -> Option<IrcMessage> {
    let sender = message.source_nickname().unwrap_or("").to_owned();
    if let ircp::Command::PRIVMSG(ref target, ref message) = message.command {
        let target = target.to_owned();
        let is_owner = owners.iter().any(|x| *x == sender);
        let sender = {
            let in_channel = target.starts_with('#');
            if !in_channel && message.contains('\x01') {
                // ignore CTCP
                return None;
            }

            MessageSource {
                chan: if in_channel { Some(target) } else { None },
                sender: sender,
                conn_hash: conn_hash.to_owned()
            }
        };

        if let Some(x) = try_cmd(&message, cmd_prefix, is_owner) {
            return Some(IrcMessage { sender: sender, data: x });
        }

        let message = message.trim();
        let tok: Vec<&str> = message.splitn(2, |c| c == '>' || c == '#' || c == '$').collect();
        if tok.len() < 2 {
            return None;
        }

        Some(IrcMessage {
            sender: sender,
            data: match &message[tok[0].len()..tok[0].len() + 1] {
                "$" => MessageData::Multiline { lang: tok[0].to_owned(), code: tok[1].to_owned() },
                ">" => MessageData::EvalReq { lang: tok[0].to_owned(), code: tok[1].to_owned(), timeout: true },
                "#" if is_owner => {
                    MessageData::EvalReq { lang: tok[0].to_owned(), code: tok[1].to_owned(), timeout: false }
                }
                _ => return None,
            }
        })
    } else {
        None
    }
}

fn try_cmd(msg: &str, cmd_prefix: &str, owner: bool) -> Option<MessageData> {
    if let Some((cmd, args)) = parse_cmd(msg, cmd_prefix) {
        match &cmd as &str {
            "quit" if owner => Some(MessageData::Quit),
            "rehash" if owner => Some(MessageData::Rehash),
            "restart" if owner && !args.is_empty() => Some(MessageData::Restart { lang: args[0].to_owned() }),
            "raw" if owner && !args.is_empty() => {
                Some(MessageData::Raw {
                    msg: {
                        let mut m = args.join(" ");
                        m.push_str("\r\n");
                        m
                    }
                })
            }
            "cancel" if !args.is_empty() => Some(MessageData::CancelMultiline { lang: args[0].to_owned() }),
            _ => None,
        }
    } else {
        None
    }
}

fn parse_cmd(msg: &str, cmd_prefix: &str) -> Option<(String, Vec<String>)> {
    let tok: Vec<&str> = match msg.splitn(2, cmd_prefix).nth(1).map(|x| x.split(' ').collect()) {
        Some(x) => x,
        None => return None,
    };

    if tok.is_empty() {
        return None;
    }

    Some((tok[0].to_owned(),
          tok.into_iter().skip(1).map(|x| x.to_owned()).collect::<Vec<String>>()))
}

fn worker(conn: NetIrcServer,
    tx: EvalWorkerSender,
    owners: Vec<String>,
    cmd_prefix: String,
    quit_handle: mpsc::Sender<()>,
    conn_hash: String) {
    'connection: loop {
        println!("connecting to to {:?} as {:?}", conn.config().server, conn.config().nickname);
        while let Err(x) = conn.identify() {
            println!("error while identify()ing; retrying: {:?}", x);
            thread::sleep(Duration::new(1, 0));
        }
        println!("connected to {:?} as {:?}", conn.config().server, conn.config().nickname);
        for msg in conn.iter() {
            let msg = match msg {
                Ok(x) => x,
                Err(_) => continue,
            };

            if let Some(msg) = parse_msg(&conn_hash, &msg, &owners, &cmd_prefix) {
                println!("M: {:?}", msg);
                match msg.data {
                    MessageData::EvalReq { .. } |
                    MessageData::Rehash |
                    MessageData::Restart { .. } |
                    MessageData::Multiline { .. } |
                    MessageData::CancelMultiline { .. } => {
                        let conn = conn.clone();
                        util::ignore(tx.send((msg, Box::new(move |to, r| send_msg!(conn, to, r)))))
                    }
                    MessageData::Raw { msg: raw_msg } => {
                        match raw_msg.parse::<ircp::Message>() {
                            Ok(raw_msg) => {
                                println!("{} sent raw: {:?}", msg.sender.sender, raw_msg);
                                if let Err(err) = conn.send(raw_msg) {
                                    send_notice!(conn, &msg.sender.sender, &format!("error: {:?}", err))
                                }
                            }
                            Err(err) => send_notice!(conn, &msg.sender.sender, &format!("error: {:?}", err)),
                        };
                    }
                    MessageData::Quit => {
                        util::ignore(quit_handle.send(()));
                        break 'connection;
                    }
                }
            }
        }
    }
}

fn eval_worker(mut svc: EvalSvc, rx: EvalWorkerReceiver, ocfg: OutputCfg) {
    let mut mlbufs: HashMap<(MessageSource, String), String> = HashMap::new();
    while let Ok((msg, callback)) = rx.recv() {
        macro_rules! reply {
            ($x:expr) => { callback(msg.sender.reply_to(), $x); }
        }
        macro_rules! key {
            ($x:expr) => { (msg.sender.clone(), $x.clone()) }
        }
        match msg.data {
            MessageData::EvalReq { lang, code, timeout } => {
                let code = match mlbufs.remove(&key!(lang)) {
                    Some(mut buf) => {
                        buf.push_str(&code);
                        buf.push_str("\n");
                        buf
                    }
                    None => code,
                };
                let ocfg = ocfg.clone();
                let sender = msg.sender.clone();
                svc.eval(&lang,
                         code,
                         timeout,
                         Some(format!("{}{}", msg.sender.conn_hash, msg.sender.reply_to())),
                         Box::new(move |resp| respond(resp, ocfg, sender, callback)))
            }
            MessageData::Rehash => {
                if let Ok(nsvc) = start_evalsvc() {
                    reply!("rehashed");
                    svc = nsvc;
                } else {
                    reply!("read config failed; check logs");
                }
            }
            MessageData::Restart { lang } => {
                match svc.restart(&lang) {
                    Ok(_) => reply!(&format!("restarted {}", lang)),
                    Err(_) => reply!(&format!("no such language {}", lang)),
                }
            }
            MessageData::Multiline { lang, code } => {
                let key = key!(lang);
                if let Some(buf) = mlbufs.get_mut(&key) {
                    buf.push_str(&code);
                    buf.push_str("\n");
                    continue;
                }
                mlbufs.insert(key, code);
            }
            MessageData::CancelMultiline { lang } => {
                match mlbufs.remove(&key!(lang)) {
                    Some(_) => reply!(&format!("{}: OK, cleared {} buffer", msg.sender.sender, lang)),
                    None => reply!(&format!("{}: no buffer for {}", msg.sender.sender, lang)),
                }
            }
            _ => println!("invalid thing sent to eval_worker?"),
        };
    }
}

fn sanitise_output(input: &str, prefix: Option<&str>, max_len: usize, max_lines: usize) -> Vec<String> {
    let (mut ret, initial_lines): (Vec<String>, usize) = {
        let med = input.lines()
                       .map(|l| l.trim_right())
                       .filter(|l| !l.is_empty())
                       .flat_map(|l| l.split(util::LengthPattern(max_len)))
                       .collect::<Vec<_>>();

        (if let Some(prefix) = prefix {
            med.iter().take(max_lines).map(|l| prefix.to_owned() + l).collect()
        } else {
            med.iter().take(max_lines).map(|l| (*l).to_owned()).collect()
        },
         med.len())
    };
    if ret.is_empty() {
        ret.push("(ok, no output)".to_owned());
    } else if ret.len() < initial_lines {
        if let Some(ref mut l) = ret.last_mut() {
            l.push_str("... (truncated)");
        }
    }
    ret
}

#[allow(boxed_local)] // send_msg is already boxed anyway
fn respond(resp: Response, cfg: OutputCfg, to: MessageSource, send_msg: SendMsgFnBox) {
    let (response, error) = match resp {
        Response::Success(x) => (x, false),
        Response::Error(x) => (x, true),
        _ => return,
    };
    let output = sanitise_output(&response,
                                 if to.is_chan() && !error { Some(&cfg.chan_output_prefix) } else { None },
                                 if to.is_chan() { cfg.max_channel_line_len } else { 425 },
                                 if to.is_chan() { cfg.max_channel_lines } else { cfg.max_priv_lines });
    for line in &output {
        send_msg(to.reply_to(), line);
    }
}
<commit_msg>Add newline to the starting multiline too<commit_after>#![feature(plugin)]
#![plugin(clippy)]

extern crate irc as irclib;
extern crate evalbotlib as backend;
extern crate rustc_serialize;

use std::thread;
use std::time::Duration;
use std::sync::mpsc;
use std::collections::HashMap;

use backend::{EvalSvc, EvalSvcCfg, Response, util};

use self::irclib::client::server::NetIrcServer;
use self::irclib::client::prelude as ircp;
use self::irclib::client::prelude::{Server, ServerExt};

macro_rules! send_msg {
    ($conn:expr, $dest:expr, $line:expr) => {
        if let Err(x) = $conn.send_privmsg($dest, $line) {
            println!("failed to send message: {}", x)
        }
    };
}

macro_rules! send_notice {
    ($conn:expr, $dest:expr, $line:expr) => {
        if let Err(x) = $conn.send_notice($dest, $line) {
            println!("failed to send notice: {}", x)
        }
    };
}

type EvalWorkerSender = mpsc::Sender<(IrcMessage, SendMsgFnBox)>;
type EvalWorkerReceiver = mpsc::Receiver<(IrcMessage, SendMsgFnBox)>;
type SendMsgFnBox = Box<Fn(&str, &str) + Send>;

#[derive(Clone, RustcDecodable, Default, PartialEq, Debug)]
struct IrcCfg {
    output: OutputCfg,
    command_prefix: String,
    owners: Vec<String>,
    irc_networks: Vec<ircp::Config>
}

#[derive(Clone, RustcDecodable, Default, PartialEq, Debug)]
struct OutputCfg {
    chan_output_prefix: String,
    max_channel_lines: usize,
    max_channel_line_len: usize,
    max_priv_lines: usize
}

fn main() {
    let config = match util::decode::<IrcCfg>("evalbot.irc.toml") {
        Ok(x) => x,
        Err(x) => {
            println!("failed to read evalbot.irc.toml: {:?}", x);
            return;
        }
    };
    let (tx, rx) = mpsc::channel();

    {
        let svc = match start_evalsvc() {
            Ok(x) => x,
            Err(()) => return,
        };
        let ocfg = config.output.clone();
        thread::spawn(move || eval_worker(svc, rx, ocfg));
    }

    let (qtx, qrx) = mpsc::channel();

    for (k, nwk) in config.irc_networks
                          .iter()
                          .filter_map(|cfg| {
                              match ircp::IrcServer::from_config(cfg.clone()) {
                                  Ok(conn) => Some(conn),
                                  Err(ref err) => {
                                      println!("failed to create IRC connection from Config {:#?}: {:?}", cfg, err);
                                      None
                                  }
                              }
                          })
                          .enumerate() {
        start_worker(nwk,
                     tx.clone(),
                     config.owners.clone(),
                     config.command_prefix.clone(),
                     qtx.clone(),
                     format!("{}", k));
    }
    util::ignore(qrx.recv());
}

fn start_evalsvc() -> Result<EvalSvc, ()> {
    let evalcfg = match util::decode::<EvalSvcCfg>("evalbot.toml") {
        Ok(x) => x,
        Err(x) => {
            println!("failed to read evalbot.toml: {:?}", x);
            return Err(());
        }
    };
    Ok(EvalSvc::new(evalcfg))
}

fn start_worker(conn: NetIrcServer,
    svc: EvalWorkerSender,
    owners: Vec<String>,
    cmd_prefix: String,
    quit_handle: mpsc::Sender<()>,
    conn_hash: String)
                -> thread::JoinHandle<()> {
    thread::spawn(move || {
        worker(conn, svc, owners, cmd_prefix, quit_handle, conn_hash);
    })
}

#[derive(Clone, Debug, Hash, PartialEq, Eq)]
struct MessageSource {
    chan: Option<String>,
    sender: String,
    conn_hash: String
}

impl MessageSource {
    fn reply_to(&self) -> &str {
        if let Some(ref chan) = self.chan { chan } else { &self.sender }
    }

    fn is_chan(&self) -> bool {
        self.chan.is_some()
    }
}

#[derive(Debug)]
struct IrcMessage {
    sender: MessageSource,
    data: MessageData
}

#[derive(Debug)]
enum MessageData {
    EvalReq {
        lang: String,
        code: String,
        timeout: bool
    },
    Quit,
    Rehash,
    Restart {
        lang: String
    },
    Raw {
        msg: String
    },
    Multiline {
        lang: String,
        code: String
    },
    CancelMultiline {
        lang: String
    }
}

unsafe impl Sync for IrcMessage {}

fn parse_msg(conn_hash: &str, message: &ircp::Message, owners: &[String], cmd_prefix: &str) -> Option<IrcMessage> {
    let sender = message.source_nickname().unwrap_or("").to_owned();
    if let ircp::Command::PRIVMSG(ref target, ref message) = message.command {
        let target = target.to_owned();
        let is_owner = owners.iter().any(|x| *x == sender);
        let sender = {
            let in_channel = target.starts_with('#');
            if !in_channel && message.contains('\x01') {
                // ignore CTCP
                return None;
            }

            MessageSource {
                chan: if in_channel { Some(target) } else { None },
                sender: sender,
                conn_hash: conn_hash.to_owned()
            }
        };

        if let Some(x) = try_cmd(&message, cmd_prefix, is_owner) {
            return Some(IrcMessage { sender: sender, data: x });
        }

        let message = message.trim();
        let tok: Vec<&str> = message.splitn(2, |c| c == '>' || c == '#' || c == '$').collect();
        if tok.len() < 2 {
            return None;
        }

        Some(IrcMessage {
            sender: sender,
            data: match &message[tok[0].len()..tok[0].len() + 1] {
                "$" => MessageData::Multiline { lang: tok[0].to_owned(), code: tok[1].to_owned() },
                ">" => MessageData::EvalReq { lang: tok[0].to_owned(), code: tok[1].to_owned(), timeout: true },
                "#" if is_owner => {
                    MessageData::EvalReq { lang: tok[0].to_owned(), code: tok[1].to_owned(), timeout: false }
                }
                _ => return None,
            }
        })
    } else {
        None
    }
}

fn try_cmd(msg: &str, cmd_prefix: &str, owner: bool) -> Option<MessageData> {
    if let Some((cmd, args)) = parse_cmd(msg, cmd_prefix) {
        match &cmd as &str {
            "quit" if owner => Some(MessageData::Quit),
            "rehash" if owner => Some(MessageData::Rehash),
            "restart" if owner && !args.is_empty() => Some(MessageData::Restart { lang: args[0].to_owned() }),
            "raw" if owner && !args.is_empty() => {
                Some(MessageData::Raw {
                    msg: {
                        let mut m = args.join(" ");
                        m.push_str("\r\n");
                        m
                    }
                })
            }
            "cancel" if !args.is_empty() => Some(MessageData::CancelMultiline { lang: args[0].to_owned() }),
            _ => None,
        }
    } else {
        None
    }
}

fn parse_cmd(msg: &str, cmd_prefix: &str) -> Option<(String, Vec<String>)> {
    let tok: Vec<&str> = match msg.splitn(2, cmd_prefix).nth(1).map(|x| x.split(' ').collect()) {
        Some(x) => x,
        None => return None,
    };

    if tok.is_empty() {
        return None;
    }

    Some((tok[0].to_owned(),
          tok.into_iter().skip(1).map(|x| x.to_owned()).collect::<Vec<String>>()))
}

fn worker(conn: NetIrcServer,
    tx: EvalWorkerSender,
    owners: Vec<String>,
    cmd_prefix: String,
    quit_handle: mpsc::Sender<()>,
    conn_hash: String) {
    'connection: loop {
        println!("connecting to to {:?} as {:?}", conn.config().server, conn.config().nickname);
        while let Err(x) = conn.identify() {
            println!("error while identify()ing; retrying: {:?}", x);
            thread::sleep(Duration::new(1, 0));
        }
        println!("connected to {:?} as {:?}", conn.config().server, conn.config().nickname);
        for msg in conn.iter() {
            let msg = match msg {
                Ok(x) => x,
                Err(_) => continue,
            };

            if let Some(msg) = parse_msg(&conn_hash, &msg, &owners, &cmd_prefix) {
                println!("M: {:?}", msg);
                match msg.data {
                    MessageData::EvalReq { .. } |
                    MessageData::Rehash |
                    MessageData::Restart { .. } |
                    MessageData::Multiline { .. } |
                    MessageData::CancelMultiline { .. } => {
                        let conn = conn.clone();
                        util::ignore(tx.send((msg, Box::new(move |to, r| send_msg!(conn, to, r)))))
                    }
                    MessageData::Raw { msg: raw_msg } => {
                        match raw_msg.parse::<ircp::Message>() {
                            Ok(raw_msg) => {
                                println!("{} sent raw: {:?}", msg.sender.sender, raw_msg);
                                if let Err(err) = conn.send(raw_msg) {
                                    send_notice!(conn, &msg.sender.sender, &format!("error: {:?}", err))
                                }
                            }
                            Err(err) => send_notice!(conn, &msg.sender.sender, &format!("error: {:?}", err)),
                        };
                    }
                    MessageData::Quit => {
                        util::ignore(quit_handle.send(()));
                        break 'connection;
                    }
                }
            }
        }
    }
}

fn eval_worker(mut svc: EvalSvc, rx: EvalWorkerReceiver, ocfg: OutputCfg) {
    let mut mlbufs: HashMap<(MessageSource, String), String> = HashMap::new();
    while let Ok((msg, callback)) = rx.recv() {
        macro_rules! reply {
            ($x:expr) => { callback(msg.sender.reply_to(), $x); }
        }
        macro_rules! key {
            ($x:expr) => { (msg.sender.clone(), $x.clone()) }
        }
        match msg.data {
            MessageData::EvalReq { lang, code, timeout } => {
                let code = match mlbufs.remove(&key!(lang)) {
                    Some(mut buf) => {
                        buf.push_str(&code);
                        buf.push_str("\n");
                        buf
                    }
                    None => code,
                };
                let ocfg = ocfg.clone();
                let sender = msg.sender.clone();
                svc.eval(&lang,
                         code,
                         timeout,
                         Some(format!("{}{}", msg.sender.conn_hash, msg.sender.reply_to())),
                         Box::new(move |resp| respond(resp, ocfg, sender, callback)))
            }
            MessageData::Rehash => {
                if let Ok(nsvc) = start_evalsvc() {
                    reply!("rehashed");
                    svc = nsvc;
                } else {
                    reply!("read config failed; check logs");
                }
            }
            MessageData::Restart { lang } => {
                match svc.restart(&lang) {
                    Ok(_) => reply!(&format!("restarted {}", lang)),
                    Err(_) => reply!(&format!("no such language {}", lang)),
                }
            }
            MessageData::Multiline { lang, mut code } => {
                let key = key!(lang);
                code.push_str("\n");
                if let Some(buf) = mlbufs.get_mut(&key) {
                    buf.push_str(&code);
                    continue;
                }
                mlbufs.insert(key, code);
            }
            MessageData::CancelMultiline { lang } => {
                match mlbufs.remove(&key!(lang)) {
                    Some(_) => reply!(&format!("{}: OK, cleared {} buffer", msg.sender.sender, lang)),
                    None => reply!(&format!("{}: no buffer for {}", msg.sender.sender, lang)),
                }
            }
            _ => println!("invalid thing sent to eval_worker?"),
        };
    }
}

fn sanitise_output(input: &str, prefix: Option<&str>, max_len: usize, max_lines: usize) -> Vec<String> {
    let (mut ret, initial_lines): (Vec<String>, usize) = {
        let med = input.lines()
                       .map(|l| l.trim_right())
                       .filter(|l| !l.is_empty())
                       .flat_map(|l| l.split(util::LengthPattern(max_len)))
                       .collect::<Vec<_>>();

        (if let Some(prefix) = prefix {
            med.iter().take(max_lines).map(|l| prefix.to_owned() + l).collect()
        } else {
            med.iter().take(max_lines).map(|l| (*l).to_owned()).collect()
        },
         med.len())
    };
    if ret.is_empty() {
        ret.push("(ok, no output)".to_owned());
    } else if ret.len() < initial_lines {
        if let Some(ref mut l) = ret.last_mut() {
            l.push_str("... (truncated)");
        }
    }
    ret
}

#[allow(boxed_local)] // send_msg is already boxed anyway
fn respond(resp: Response, cfg: OutputCfg, to: MessageSource, send_msg: SendMsgFnBox) {
    let (response, error) = match resp {
        Response::Success(x) => (x, false),
        Response::Error(x) => (x, true),
        _ => return,
    };
    let output = sanitise_output(&response,
                                 if to.is_chan() && !error { Some(&cfg.chan_output_prefix) } else { None },
                                 if to.is_chan() { cfg.max_channel_line_len } else { 425 },
                                 if to.is_chan() { cfg.max_channel_lines } else { cfg.max_priv_lines });
    for line in &output {
        send_msg(to.reply_to(), line);
    }
}
<|endoftext|>


<commit_before>use std::time::Duration;

use smithay::{
    backend::{
        renderer::gles2::Gles2Renderer,
        winit::{self, WinitError, WinitEvent, WinitEventLoop, WinitGraphicsBackend},
    },
    desktop::space::SurfaceTree,
    output::{Mode, Output, PhysicalProperties, Subpixel},
    reexports::calloop::{
        timer::{TimeoutAction, Timer},
        EventLoop,
    },
    utils::{Rectangle, Transform},
};

use slog::Logger;

use crate::{CalloopData, Smallvil};

pub fn init_winit(
    event_loop: &mut EventLoop<CalloopData>,
    data: &mut CalloopData,
    log: Logger,
) -> Result<(), Box<dyn std::error::Error>> {
    let display = &mut data.display;
    let state = &mut data.state;

    let (mut backend, mut winit) = winit::init(log.clone())?;

    let mode = Mode {
        size: backend.window_size().physical_size,
        refresh: 60_000,
    };

    let output = Output::new::<_>(
        "winit".to_string(),
        PhysicalProperties {
            size: (0, 0).into(),
            subpixel: Subpixel::Unknown,
            make: "Smithay".into(),
            model: "Winit".into(),
        },
        log.clone(),
    );
    let _global = output.create_global::<Smallvil>(&display.handle());
    output.change_current_state(Some(mode), Some(Transform::Flipped180), None, Some((0, 0).into()));
    output.set_preferred(mode);

    state.space.map_output(&output, (0, 0));

    std::env::set_var("WAYLAND_DISPLAY", &state.socket_name);

    let mut full_redraw = 0u8;

    let timer = Timer::immediate();
    event_loop.handle().insert_source(timer, move |_, _, data| {
        winit_dispatch(&mut backend, &mut winit, data, &output, &mut full_redraw).unwrap();
        TimeoutAction::ToDuration(Duration::from_millis(16))
    })?;

    Ok(())
}

pub fn winit_dispatch(
    backend: &mut WinitGraphicsBackend,
    winit: &mut WinitEventLoop,
    data: &mut CalloopData,
    output: &Output,
    full_redraw: &mut u8,
) -> Result<(), Box<dyn std::error::Error>> {
    let display = &mut data.display;
    let state = &mut data.state;

    let res = winit.dispatch_new_events(|event| match event {
        WinitEvent::Resized { size, .. } => {
            output.change_current_state(
                Some(Mode {
                    size,
                    refresh: 60_000,
                }),
                None,
                None,
                None,
            );
        }
        WinitEvent::Input(event) => state.process_input_event(event),
        _ => (),
    });

    if let Err(WinitError::WindowClosed) = res {
        // Stop the loop
        state.loop_signal.stop();

        return Ok(());
    } else {
        res?;
    }

    *full_redraw = full_redraw.saturating_sub(1);

    let size = backend.window_size().physical_size;
    let damage = Rectangle::from_loc_and_size((0, 0), size);

    backend.bind().ok().and_then(|_| {
        state
            .space
            .render_output::<Gles2Renderer, SurfaceTree>(
                backend.renderer(),
                output,
                0,
                [0.1, 0.1, 0.1, 1.0],
                &[],
            )
            .unwrap()
    });

    backend.submit(Some(&[damage])).unwrap();

    state
        .space
        .send_frames(state.start_time.elapsed().as_millis() as u32);

    state.space.refresh(&display.handle());
    display.flush_clients()?;

    Ok(())
}
<commit_msg>update smallvil<commit_after>use std::time::Duration;

use smithay::{
    backend::{
        renderer::{gles2::Gles2Renderer, output::OutputRender},
        winit::{self, WinitError, WinitEvent, WinitEventLoop, WinitGraphicsBackend},
    },
    desktop::space::{SpaceRenderElements, SurfaceTree},
    output::{Mode, Output, PhysicalProperties, Subpixel},
    reexports::calloop::{
        timer::{TimeoutAction, Timer},
        EventLoop,
    },
    utils::{Rectangle, Transform},
};

use slog::Logger;

use crate::{CalloopData, Smallvil};

pub fn init_winit(
    event_loop: &mut EventLoop<CalloopData>,
    data: &mut CalloopData,
    log: Logger,
) -> Result<(), Box<dyn std::error::Error>> {
    let display = &mut data.display;
    let state = &mut data.state;

    let (mut backend, mut winit) = winit::init(log.clone())?;

    let mode = Mode {
        size: backend.window_size().physical_size,
        refresh: 60_000,
    };

    let output = Output::new::<_>(
        "winit".to_string(),
        PhysicalProperties {
            size: (0, 0).into(),
            subpixel: Subpixel::Unknown,
            make: "Smithay".into(),
            model: "Winit".into(),
        },
        log.clone(),
    );
    let _global = output.create_global::<Smallvil>(&display.handle());
    output.change_current_state(Some(mode), Some(Transform::Flipped180), None, Some((0, 0).into()));
    output.set_preferred(mode);

    state.space.map_output(&output, (0, 0));

    let mut output_renderer = OutputRender::new(&output);

    std::env::set_var("WAYLAND_DISPLAY", &state.socket_name);

    let mut full_redraw = 0u8;

    let timer = Timer::immediate();
    event_loop.handle().insert_source(timer, move |_, _, data| {
        winit_dispatch(
            &mut backend,
            &mut winit,
            data,
            &output,
            &mut output_renderer,
            &mut full_redraw,
            &log,
        )
        .unwrap();
        TimeoutAction::ToDuration(Duration::from_millis(16))
    })?;

    Ok(())
}

pub fn winit_dispatch(
    backend: &mut WinitGraphicsBackend,
    winit: &mut WinitEventLoop,
    data: &mut CalloopData,
    output: &Output,
    output_render: &mut OutputRender,
    full_redraw: &mut u8,
    log: &Logger,
) -> Result<(), Box<dyn std::error::Error>> {
    let display = &mut data.display;
    let state = &mut data.state;

    let res = winit.dispatch_new_events(|event| match event {
        WinitEvent::Resized { size, .. } => {
            output.change_current_state(
                Some(Mode {
                    size,
                    refresh: 60_000,
                }),
                None,
                None,
                None,
            );
        }
        WinitEvent::Input(event) => state.process_input_event(event),
        _ => (),
    });

    if let Err(WinitError::WindowClosed) = res {
        // Stop the loop
        state.loop_signal.stop();

        return Ok(());
    } else {
        res?;
    }

    *full_redraw = full_redraw.saturating_sub(1);

    let size = backend.window_size().physical_size;
    let damage = Rectangle::from_loc_and_size((0, 0), size);

    backend.bind().ok().and_then(|_| {
        smithay::desktop::space::render_output::<
            Gles2Renderer,
            SurfaceTree,
            SpaceRenderElements<Gles2Renderer>,
        >(
            backend.renderer(),
            0,
            &[(&state.space, &[])],
            &[],
            output_render,
            log,
        )
        .unwrap()
    });

    backend.submit(Some(&[damage])).unwrap();

    state
        .space
        .send_frames(state.start_time.elapsed().as_millis() as u32);

    state.space.refresh(&display.handle());
    display.flush_clients()?;

    Ok(())
}
<|endoftext|>


<commit_before>extern crate thunderdome;

#[cfg(test)]
mod property_tests {

    use thunderdome::graph::*;
    use thunderdome::property::*;

    #[test]
    fn test_get_set_compare_properties() {

    }

    #[test]
    fn test_properties() {
        let mut g = Graph::new();
        let mut v1 = g.add_vertex();
        let key = &"jon";
        let value = Property::Int(1);
        v1.set_property(&key, value);

        let result = v1.get_property(&key).unwrap();

        match result {
            &Property::Int(1) =>
                // we're ok
                println!("OK"),
            _ =>
                panic!("wrong graph property")
        }
    }

    #[test]
    fn test_has() {
        let mut g = Graph::new();
        let mut v = g.add_vertex();
        let mut v2 = g.add_vertex();
        v.add_edge(&mut v2, &"friends");

        let pizza = Property::String("pizza".to_string());
        v2.set_property("favorite_food", pizza.clone());

        let result = g.v(1).
                        outV(&[&"friends"]).
                        has("favorite_food", pizza.clone());

        // assert_eq!(result.paths.len(), 1);

    }

}
<commit_msg>removed unneeded amp<commit_after>extern crate thunderdome;

#[cfg(test)]
mod property_tests {

    use thunderdome::graph::*;
    use thunderdome::property::*;

    #[test]
    fn test_get_set_compare_properties() {

    }

    #[test]
    fn test_properties() {
        let mut g = Graph::new();
        let mut v1 = g.add_vertex();
        let key = &"jon";
        let value = Property::Int(1);
        v1.set_property(&key, value);

        let result = v1.get_property(&key).unwrap();

        match result {
            &Property::Int(1) =>
                // we're ok
                println!("OK"),
            _ =>
                panic!("wrong graph property")
        }
    }

    #[test]
    fn test_has() {
        let mut g = Graph::new();
        let mut v = g.add_vertex();
        let mut v2 = g.add_vertex();
        v.add_edge(&mut v2, &"friends");

        let pizza = Property::String("pizza".to_string());
        v2.set_property("favorite_food", pizza.clone());

        let result = g.v(1).
                        outV(&["friends"]).
                        has("favorite_food", pizza.clone());

        // assert_eq!(result.paths.len(), 1);

    }

}
<|endoftext|>