<commit_before>//! Generate the site content.

// Standard library
use std::borrow::Cow::{Borrowed, Owned};
use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf};

// Third party
use glob::{glob, Paths};
use pulldown_cmark as cmark;
use pulldown_cmark::{Event, Parser, Tag};
use syntect::highlighting::ThemeSet;

// First party
use crate::config::{Config, SyntaxOption};

/// Load the `Paths` for all markdown files in the specified content directory.
///
/// If the specified configuration values are wrong, we return a vector of all
/// the errors in the config as far as this is concerned.
///
/// TODO: move that validation elsewhere? Should probably extract a more general
/// site configuration validator.
fn load_markdown_paths(site_directory: &PathBuf, config: &Config) -> Result<Vec<PathBuf>, String> {
    let site = site_directory
        .to_str()
        .ok_or(format!("bad `site`: {:?}", site_directory));

    let directories = config.directories.content.to_str().ok_or(format!(
        "bad content directory: {:?}",
        config.directories.content
    ));

    let (site, directories) = match (site, directories) {
        (Err(s), Err(d)) => Err(s + "\n" + &d),
        (Err(s), Ok(_)) => Err(s),
        (Ok(_), Err(d)) => Err(d),
        (Ok(s), Ok(d)) => Ok((s, d)),
    }?;

    let content_glob = format!("{}/{}/**/*.md", site, directories);

    let paths = glob(&content_glob).map_err(|err| format!("{:?}", err))?;

    let (valid, errors): (Vec<Result<PathBuf, _>>, Vec<Result<_, String>>) = paths
        .map(|glob_result| glob_result.map_err(|err| format!("{:?}", err)))
        .partition(|path| path.is_ok());

    let errors: Vec<String> = errors.into_iter().map(|e| e.unwrap_err()).collect();
    if errors.len() == 0 {
        Ok(valid.into_iter().map(|v| v.unwrap()).collect())
    } else {
        Err(errors.join(",\n"))
    }
}

// TODO: this is an obvious candidate for a `HashMap`, so use that instead.
type LoadTuple<'a> = (&'a PathBuf, Result<String, String>);

fn load_content<'p>(paths: &'p [PathBuf]) -> Result<Vec<(&'p PathBuf, String)>, String> {
    let (contents, errs): (Vec<LoadTuple>, Vec<LoadTuple>) = paths
        .into_iter()
        .map(|path| {
            let content = std::fs::read_to_string(path).map_err(|e| format!("{:?}", e));
            (path, content)
        })
        .partition(|(_, result)| result.is_ok());

    if errs.len() == 0 {
        Ok(contents
            .into_iter()
            .map(|(path, contents)| (path, contents.unwrap()))
            .collect())
    } else {
        Err(errs
            .into_iter()
            .map(|(_path, err)| err.unwrap_err())
            .collect::<Vec<String>>()
            .join(",\n"))
    }
}

/// Load the templates associated with each taxonomy.
fn load_templates(_site_directory: &PathBuf, _config: &Config) -> Result<Paths, String> {
    unimplemented!()
}

// TODO: put this somewhere else when extracting all the parser logic below into
// its own module.
#[derive(Debug)]
enum ParseState<'s> {
    CodeBlock(&'s syntect::parsing::SyntaxReference),
    PlainTextBlock,
    NonCode,
}

impl<'s> std::default::Default for ParseState<'s> {
    fn default() -> ParseState<'s> {
        ParseState::NonCode
    }
}

/// Generate content from a configuration.
pub fn build(site_directory: PathBuf) -> Result<(), String> {
    // NOTE: this is almost certainly not what we *ultimately* want here, but
    // it'll do as a starting point. There's a lot of `into_iter` and `collect`
    // happening here, which forces us to eagerly materialize a lot of these
    // collections rather than being able to deal with them in a more
    // "streaming" fashion.
    let config = Config::from_file(&PathBuf::from(&site_directory))?;
    let markdown_paths = load_markdown_paths(&site_directory, &config)?;
    let contents = load_content(&markdown_paths)?;

    // TODO: migrate this logic out elsewhere
    // TODO: when doing ^ take into account that we want to be able to avoid a
    // bunch of extra layers of indirection here. If we don't have a theme, that
    // translates to being in `SyntaxOption::Off` or `SyntaxOption::TagOnly`,
    // since the case where we can't *load* the theme is an error. What I want,
    // therefore, is to reflect that in the parsing behavior later.
    let theme = match &config.options.syntax {
        SyntaxOption::Highlight(theme) => {
            let theme_file = PathBuf::from("data").join(theme);
            let theme = ThemeSet::get_theme(theme_file).map_err(|err| format!("{:?}", err))?;
            Some(theme)
        }
        SyntaxOption::Off | SyntaxOption::TagOnly => None,
    };

    // TODO: generate these! Best move: on build, generate a `.packdump`, since
    // that seems to be what Syntect knows how to deal with.
    let syntax_dir = PathBuf::from("data/syntaxes");
    let syntax_set =
        syntect::parsing::SyntaxSet::load_from_folder(syntax_dir).expect("can't load syntaxes");

    let parsed_content: Vec<(&PathBuf, String)> = contents
        .into_iter()
        .map(|(path, content)| {
            let parser = Parser::new_ext(&content, pulldown_cmark::Options::all()).scan(
                ParseState::default(),
                |state, event| match event {
                    Event::Start(Tag::CodeBlock(ref language)) => match state {
                        ParseState::NonCode => {
                            *state = match syntax_set.find_syntax_by_name(&language) {
                                Some(syntax) => ParseState::CodeBlock(syntax),
                                None => ParseState::PlainTextBlock,
                            };

                            // We start every code block with `<pre><code class="...">`
                            // so that we always have semantically correct HTML. However, we
                            // don't add the language class if the language isn't set.
                            let content = if language.len() > 0 {
                                Owned(format!(r#"
"#, language))
                            } else {
                                Borrowed("
")
                            };

                            Some(Event::Html(content))
                        }
                        ParseState::CodeBlock(_) | ParseState::PlainTextBlock => {
                            unreachable!("Bad event/state: {:?} with {:?}", event, state);
                        }
                    },

                    Event::End(Tag::CodeBlock(_)) => match state {
                        ParseState::CodeBlock(_) | ParseState::PlainTextBlock => {
                            *state = ParseState::NonCode;
                            Some(Event::Html(Borrowed("
")))
                        }
                        ParseState::NonCode => {
                            unreachable!("Bad event/state: {:?} with {:?}", event, state);
                        }
                    },

                    // When we are in a text block, we *may* be in a code block,
                    // so we may also need to do whatever highlighting is
                    // specified.
                    Event::Text(s) => {
                        match state {
                            ParseState::CodeBlock(syntax) => match &theme {
                                Some(theme) => {
                                    let highlighted = syntect::html::highlighted_html_for_string(
                                        &s,
                                        &syntax_set,
                                        &syntax,
                                        theme,
                                    );

                                    Some(Event::Text(Owned(highlighted)))
                                }
                                None => {
                                    // TODO: implement tag-only parsing with syntect
                                    unimplemented!()
                                }
                            },
                            ParseState::PlainTextBlock => Some(Event::Text(s)),
                            ParseState::NonCode => Some(Event::Text(s)),
                        }
                    }

                    // TODO: accumulate footnote references and always put them
                    // at the end. Also, generate back-links for them, unless or
                    // until pulldown-cmark does so.
                    Event::FootnoteReference(s) => Some(Event::FootnoteReference(s)),

                    Event::Start(s) => Some(Event::Start(s)),
                    Event::End(e) => Some(Event::End(e)),
                    Event::InlineHtml(s) => Some(Event::InlineHtml(s)),
                    Event::Html(h) => Some(Event::Html(h)),
                    Event::SoftBreak => Some(Event::SoftBreak),
                    Event::HardBreak => Some(Event::HardBreak),
                },
            );

            // These numbers are derived from a brief survey of content in my
            // own website historically, including both code-heavy and
            // prose-heavy posts. It's probably worth exploring other corpora
            // to see if this is basically right.
            let length_bound_estimate = match &config.options.syntax {
                SyntaxOption::Off | SyntaxOption::TagOnly => 2,
                SyntaxOption::Highlight(_) => 8, // TODO: check other pages
            };

            let mut html = String::with_capacity(content.len() * length_bound_estimate);
            cmark::html::push_html(&mut html, parser);

            (path, html)
        })
        .collect();

    // TODO: extract this as part of the writing it out process.
    let output_dir = site_directory.join(&config.directories.output);

    if output_dir.exists() && !output_dir.is_dir() {
        return Err(format!("Output directory {:?} already exists!", output_dir));
    }

    if !output_dir.exists() {
        std::fs::create_dir(&output_dir).expect(&format!(
            "could not create output directory {:?}",
            &output_dir
        ));
    }

    for (path, content) in parsed_content {
        let file_path = path
            .file_name()
            .ok_or(format!("invalid file: {:?}", path))?;

        let dest = output_dir.join(file_path).with_extension("html");

        let mut fd = match File::create(&dest) {
            Ok(file) => file,
            Err(reason) => {
                return Err(format!(
                    "Could not open {} for write: {}",
                    dest.to_string_lossy(),
                    reason
                ));
            }
        };

        let result = write!(fd, "{}", content);
        if let Err(reason) = result {
            return Err(format!("{:?}", reason.kind()));
        }
    }

    Ok(())
}
<commit_msg>improvement: simplify tracking syntax rule.<commit_after>//! Generate the site content.

// Standard library
use std::borrow::Cow::{Borrowed, Owned};
use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf};

// Third party
use glob::{glob, Paths};
use pulldown_cmark as cmark;
use pulldown_cmark::{Event, Parser, Tag};
use syntect::highlighting::ThemeSet;

// First party
use crate::config::{Config, SyntaxOption};

/// Load the `Paths` for all markdown files in the specified content directory.
///
/// If the specified configuration values are wrong, we return a vector of all
/// the errors in the config as far as this is concerned.
///
/// TODO: move that validation elsewhere? Should probably extract a more general
/// site configuration validator.
fn load_markdown_paths(site_directory: &PathBuf, config: &Config) -> Result<Vec<PathBuf>, String> {
    let site = site_directory
        .to_str()
        .ok_or(format!("bad `site`: {:?}", site_directory));

    let directories = config.directories.content.to_str().ok_or(format!(
        "bad content directory: {:?}",
        config.directories.content
    ));

    let (site, directories) = match (site, directories) {
        (Err(s), Err(d)) => Err(s + "\n" + &d),
        (Err(s), Ok(_)) => Err(s),
        (Ok(_), Err(d)) => Err(d),
        (Ok(s), Ok(d)) => Ok((s, d)),
    }?;

    let content_glob = format!("{}/{}/**/*.md", site, directories);

    let paths = glob(&content_glob).map_err(|err| format!("{:?}", err))?;

    let (valid, errors): (Vec<Result<PathBuf, _>>, Vec<Result<_, String>>) = paths
        .map(|glob_result| glob_result.map_err(|err| format!("{:?}", err)))
        .partition(|path| path.is_ok());

    let errors: Vec<String> = errors.into_iter().map(|e| e.unwrap_err()).collect();
    if errors.len() == 0 {
        Ok(valid.into_iter().map(|v| v.unwrap()).collect())
    } else {
        Err(errors.join(",\n"))
    }
}

// TODO: this is an obvious candidate for a `HashMap`, so use that instead.
type LoadTuple<'a> = (&'a PathBuf, Result<String, String>);

fn load_content<'p>(paths: &'p [PathBuf]) -> Result<Vec<(&'p PathBuf, String)>, String> {
    let (contents, errs): (Vec<LoadTuple>, Vec<LoadTuple>) = paths
        .into_iter()
        .map(|path| {
            let content = std::fs::read_to_string(path).map_err(|e| format!("{:?}", e));
            (path, content)
        })
        .partition(|(_, result)| result.is_ok());

    if errs.len() == 0 {
        Ok(contents
            .into_iter()
            .map(|(path, contents)| (path, contents.unwrap()))
            .collect())
    } else {
        Err(errs
            .into_iter()
            .map(|(_path, err)| err.unwrap_err())
            .collect::<Vec<String>>()
            .join(",\n"))
    }
}

/// Load the templates associated with each taxonomy.
fn load_templates(_site_directory: &PathBuf, _config: &Config) -> Result<Paths, String> {
    unimplemented!()
}

// TODO: put this somewhere else when extracting all the parser logic below into
// its own module.
#[derive(Debug)]
enum ParseState<'s> {
    CodeBlock(&'s syntect::parsing::SyntaxReference),
    PlainTextBlock,
    NonCode,
}

impl<'s> std::default::Default for ParseState<'s> {
    fn default() -> ParseState<'s> {
        ParseState::NonCode
    }
}

enum Syntax {
    Highlight(syntect::highlighting::Theme),
    TagOnly,
    Off,
}

impl Syntax {
    fn try_from(config_syntax: &SyntaxOption) -> Result<Syntax, String> {
        match config_syntax {
            SyntaxOption::Highlight(theme) => {
                let theme_file = PathBuf::from("data").join(theme);
                let theme = ThemeSet::get_theme(theme_file).map_err(|err| format!("{:?}", err))?;
                Ok(Syntax::Highlight(theme))
            }
            SyntaxOption::TagOnly => Ok(Syntax::TagOnly),
            SyntaxOption::Off => Ok(Syntax::Off),
        }
    }
}

/// Generate content from a configuration.
pub fn build(site_directory: PathBuf) -> Result<(), String> {
    // NOTE: this is almost certainly not what we *ultimately* want here, but
    // it'll do as a starting point. There's a lot of `into_iter` and `collect`
    // happening here, which forces us to eagerly materialize a lot of these
    // collections rather than being able to deal with them in a more
    // "streaming" fashion.
    let config = Config::from_file(&PathBuf::from(&site_directory))?;
    let markdown_paths = load_markdown_paths(&site_directory, &config)?;
    let contents = load_content(&markdown_paths)?;

    let configured_syntax = Syntax::try_from(&config.options.syntax)?;

    // TODO: generate these! Best move: on build, generate a `.packdump`, since
    // that seems to be what Syntect knows how to deal with.
    let syntax_dir = PathBuf::from("data/syntaxes");
    let syntax_set =
        syntect::parsing::SyntaxSet::load_from_folder(syntax_dir).expect("can't load syntaxes");

    let parsed_content: Vec<(&PathBuf, String)> = contents
        .into_iter()
        .map(|(path, content)| {
            let parser = Parser::new_ext(&content, pulldown_cmark::Options::all()).scan(
                ParseState::default(),
                |state, event| match event {
                    Event::Start(Tag::CodeBlock(ref language)) => match state {
                        ParseState::NonCode => {
                            *state = match syntax_set.find_syntax_by_name(&language) {
                                Some(syntax) => ParseState::CodeBlock(syntax),
                                None => ParseState::PlainTextBlock,
                            };

                            // We start every code block with `<pre><code class="...">`
                            // so that we always have semantically correct HTML. However, we
                            // don't add the language class if the language isn't set.
                            let content = if language.len() > 0 {
                                Owned(format!(r#"
"#, language))
                            } else {
                                Borrowed("
")
                            };

                            Some(Event::Html(content))
                        }
                        ParseState::CodeBlock(_) | ParseState::PlainTextBlock => {
                            unreachable!("Bad event/state: {:?} with {:?}", event, state);
                        }
                    },

                    Event::End(Tag::CodeBlock(_)) => match state {
                        ParseState::CodeBlock(_) | ParseState::PlainTextBlock => {
                            *state = ParseState::NonCode;
                            Some(Event::Html(Borrowed("
")))
                        }
                        ParseState::NonCode => {
                            unreachable!("Bad event/state: {:?} with {:?}", event, state);
                        }
                    },

                    // When we are in a text block, we *may* be in a code block,
                    // so we may also need to do whatever highlighting is
                    // specified.
                    Event::Text(s) => {
                        match state {
                            ParseState::CodeBlock(syntax) => match &configured_syntax {
                                Syntax::Highlight(theme) => {
                                    let highlighted = syntect::html::highlighted_html_for_string(
                                        &s,
                                        &syntax_set,
                                        &syntax,
                                        theme,
                                    );

                                    Some(Event::Text(Owned(highlighted)))
                                }
                                Syntax::TagOnly => {
                                    // TODO: implement tag-only parsing with syntect
                                    unimplemented!()
                                }
                                Syntax::Off => Some(Event::Text(s)),
                            },
                            ParseState::PlainTextBlock => Some(Event::Text(s)),
                            ParseState::NonCode => Some(Event::Text(s)),
                        }
                    }

                    // TODO: accumulate footnote references and always put them
                    // at the end. Also, generate back-links for them, unless or
                    // until pulldown-cmark does so.
                    Event::FootnoteReference(s) => Some(Event::FootnoteReference(s)),

                    Event::Start(s) => Some(Event::Start(s)),
                    Event::End(e) => Some(Event::End(e)),
                    Event::InlineHtml(s) => Some(Event::InlineHtml(s)),
                    Event::Html(h) => Some(Event::Html(h)),
                    Event::SoftBreak => Some(Event::SoftBreak),
                    Event::HardBreak => Some(Event::HardBreak),
                },
            );

            // These numbers are derived from a brief survey of content in my
            // own website historically, including both code-heavy and
            // prose-heavy posts. It's probably worth exploring other corpora
            // to see if this is basically right.
            let length_bound_estimate = match &config.options.syntax {
                SyntaxOption::Off | SyntaxOption::TagOnly => 2,
                SyntaxOption::Highlight(_) => 8, // TODO: check other pages
            };

            let mut html = String::with_capacity(content.len() * length_bound_estimate);
            cmark::html::push_html(&mut html, parser);

            (path, html)
        })
        .collect();

    // TODO: extract this as part of the writing it out process.
    let output_dir = site_directory.join(&config.directories.output);

    if output_dir.exists() && !output_dir.is_dir() {
        return Err(format!("Output directory {:?} already exists!", output_dir));
    }

    if !output_dir.exists() {
        std::fs::create_dir(&output_dir).expect(&format!(
            "could not create output directory {:?}",
            &output_dir
        ));
    }

    for (path, content) in parsed_content {
        let file_path = path
            .file_name()
            .ok_or(format!("invalid file: {:?}", path))?;

        let dest = output_dir.join(file_path).with_extension("html");

        let mut fd = match File::create(&dest) {
            Ok(file) => file,
            Err(reason) => {
                return Err(format!(
                    "Could not open {} for write: {}",
                    dest.to_string_lossy(),
                    reason
                ));
            }
        };

        let result = write!(fd, "{}", content);
        if let Err(reason) = result {
            return Err(format!("{:?}", reason.kind()));
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>use config::Region;
use color::{RGB8, BGRA8};

use libc::{c_void,
	uint8_t,
	size_t};
use std::ptr;
use std::mem;

/// Enum of results returned by DXGCap
#[repr(C)]
#[allow(dead_code)]
enum DXGCaptureResult {
	Ok,
	// Could not duplicate output, access denied. Might be in protected fullscreen.
	AccessDenied,
	// Access to the duplicated output was lost. Likely, mode was changed e.g. window => full
	AccessLost,
	// Error when trying to refresh outputs after some failure.
	RefreshFailure,
	// AcquireNextFrame timed out.
	Timeout,
	// General/Unexpected failure
	Fail,
}

#[link(name = "DXGCap")]
extern {
	fn init();

	fn uninit();

	fn create_dxgi_manager() -> *mut c_void;

	fn delete_dxgi_manager(dxgi_manager: *mut c_void);

	fn set_timeout(dxgi_manager: *mut c_void, timeout: u32);

	fn set_capture_source(dxgi_manager: *mut c_void, cs: u16);

	fn get_capture_source(dxgi_manager: *mut c_void) -> u16;

	fn refresh_output(dxgi_manager: *mut c_void) -> bool;

	fn get_output_dimensions(dxgi_manager: *const c_void, width: *mut u32, height: *mut u32);

	// Returns DXGI status code, HRESULT
	fn get_frame_bytes(dxgi_manager: *mut c_void, o_size: *mut size_t,
		o_bytes: *mut *mut uint8_t) -> DXGCaptureResult;
}

/// Possible errors when capturing
#[allow(dead_code)]
pub enum CaptureError {
	// Could not duplicate output, access denied. Might be in protected fullscreen.
	AccessDenied,
	// Access to the duplicated output was lost. Likely, mode was changed e.g. window => full
	AccessLost,
	// Error when trying to refresh outputs after some failure.
	RefreshFailure,
	// AcquireNextFrame timed out.
	Timeout,
	// General/Unexpected failure
	Fail,
}
impl CaptureError {
	/// Try to represent the DXGCap capture result as a CaptureError
	fn from_dxgcapture_result(cr: DXGCaptureResult) -> Option<CaptureError> {
		use self::DXGCaptureResult as D;
		match cr {
			D::Ok => None,
			D::AccessDenied => Some(CaptureError::AccessDenied),
			D::AccessLost => Some(CaptureError::AccessLost),
			D::RefreshFailure => Some(CaptureError::RefreshFailure),
			D::Timeout => Some(CaptureError::Timeout),
			D::Fail => Some(CaptureError::Fail),
		}
	}
}

static DXGI_PIXEL_SIZE: u64 = 4; // BGRA8 => 4 bytes, DXGI default

/// Initiate windows stuff that DXGCap requires.
fn init_dxgi() {
	unsafe { init(); }
}
fn uninit_dxgi() {
	unsafe { uninit(); }
}

/// Representation of an image as a vector of 32-bit BGRA pixels,
/// coupled with image dimensions
#[derive(Clone)]
pub struct Image {
	width: u32, height: u32,
	pixels: Vec<BGRA8>,
}
impl Image {
	fn new() -> Image {
		Image{ width: 0, height: 0, pixels: Vec::new() }
	}
}

/// An analyzer with a slot for an image to be analyzed. `resize_width` and `resize_height` specify
/// what resolution to use when analyzing the image. Often this decides number of rows/cols to skip.
/// Analysis that may be done is such as calculating average color of a region of the image.
#[derive(Clone)]
pub struct ImageAnalyzer {
	image: Image,
	resize_width: u32, resize_height: u32,
}
impl ImageAnalyzer {
	pub fn new() -> ImageAnalyzer {
		ImageAnalyzer{ image: Image::new(), resize_width: 1, resize_height: 1 }
	}

	/// Swap the image slotted in the analyzer for a new one. Return the old one.
	pub fn swap_slotted(&mut self, new: Image) -> Image {
		mem::replace(&mut self.image, new)
	}

	/// Change the dimensions to work with when analyzing
	pub fn set_resize_dimensions(&mut self, (resize_width, resize_height): (u32, u32)) {
		self.resize_width = if resize_width == 0 {
			self.image.width
		} else {
			resize_width
		};
		self.resize_height = if resize_height == 0 {
			self.image.height
		} else {
			resize_height
		};
	}

	/// Calculate the average color of `self.image` for region given by `led`
	pub fn average_color(&self, led: &Region) -> RGB8 {
		if self.image.pixels.len() == 0 {
			RGB8{ r: 0, g: 0, b: 0 }
		} else {
			let (resize_width_ratio, resize_height_ratio) = (
				self.image.width as f32 / self.resize_width as f32,
				self.image.height as f32 / self.resize_height as f32);
			let (y1, y2, x1, x2) = (
				(led.vscan.minimum * self.resize_height as f32) as usize,
				(led.vscan.maximum * self.resize_height as f32) as usize,
				(led.hscan.minimum * self.resize_width as f32) as usize,
				(led.hscan.maximum * self.resize_width as f32) as usize);
			let (mut r_sum, mut g_sum, mut b_sum) = (0u64, 0u64, 0u64);
			for row in y1..y2 {
				for col in x1..x2 {
					let pixel = &self.image.pixels[(
						row as f32 * resize_height_ratio *
						self.image.width as f32 +
						col as f32 * resize_width_ratio) as usize];
					r_sum += pixel.r as u64;
					g_sum += pixel.g as u64;
					b_sum += pixel.b as u64;
				}
			}

			let n_of_pixels = ((x2 - x1) * (y2 - y1)) as u64;
			RGB8{r: (r_sum/n_of_pixels) as u8,
				g: (g_sum/n_of_pixels) as u8,
				b: (b_sum/n_of_pixels) as u8 }
		}
	}
}

/// A screen capturer for capturing the contents of a monitor.
/// Currently this is not much more than a ffi wrapper for the DXGCap dxgi manager,
/// and, as such, does not support capturing many fullscreen games, especially directx ones.
pub struct Capturer {
	dxgi_manager: *mut c_void,
}
impl Capturer {
	/// Initialize DXGCap and construct a new `Capturer` with a new dxgi manager
	pub fn new() -> Capturer {
		init_dxgi();
		let manager = unsafe { create_dxgi_manager() };
		if manager.is_null() {
			uninit_dxgi();
			panic!("Unexpected null pointer when constructing Capturer.")
		} else {
			Capturer{ dxgi_manager: manager }
		}
	}

	/// Specify the amount of time to wait for new frame before returning.
	pub fn set_timeout(&mut self, timeout: u32) {
		unsafe { set_timeout(self.dxgi_manager, timeout) }
	}

	/// Specify which monitor to capture from.
	/// A value of `0` results in capture of primary monitor.
	/// In windows, value of `cs` corresponds to monitor IDs in `Display\Screen Resolution`
	#[allow(dead_code)]
	pub fn set_capture_source(&mut self, cs: u16) {
		unsafe { set_capture_source(self.dxgi_manager, cs) }
	}

	/// Return index of current capture source
	#[allow(dead_code)]
	pub fn get_capture_source(&mut self) -> u16 {
		unsafe { get_capture_source(self.dxgi_manager) }
	}

	/// Manually refresh dxgi output adapters. May be needed if access to desktop duplication
	/// is lost, but DXGCap did not fix it automatically.
	pub fn refresh_output(&mut self) -> bool {
		unsafe { refresh_output(self.dxgi_manager) }
	}

	/// Get the display resolution of selected output device.
	pub fn get_display_resolution(&self) -> (u32, u32) {
		let (mut width, mut height) = (0, 0);
		unsafe { get_output_dimensions(self.dxgi_manager, &mut width, &mut height); }
		(width, height)
	}

	/// Capture a frame from the capture source, and return captured bytes as an `Image`.
	/// On success, return an `Image`, otherwise, return the CaptureResult indicating error type
	pub fn capture_frame(&mut self) -> Result<Image, CaptureError> {
		let mut shared_buf_size: size_t = 0;
		let mut shared_buf = ptr::null_mut::<u8>();

		let cr = unsafe{
			get_frame_bytes(self.dxgi_manager, &mut shared_buf_size, &mut shared_buf) };
		if let DXGCaptureResult::Ok = cr  {
			if shared_buf.is_null() {
				Err(CaptureError::Fail)
			} else {
				let n_pixels = (shared_buf_size / DXGI_PIXEL_SIZE) as usize;
				let mut pixel_buf: Vec<BGRA8> = Vec::with_capacity(n_pixels);

				unsafe {
					ptr::copy(pixel_buf.as_mut_ptr(),
						shared_buf as *const BGRA8,
						n_pixels);
					pixel_buf.set_len(n_pixels);
				}

				let (width, height) = self.get_display_resolution();

				Ok(Image{ width: width, height: height, pixels: pixel_buf })
			}
		} else {
			Err(CaptureError::from_dxgcapture_result(cr)
				.expect("DXGCaptureResult was not an error"))
		}
	}
}
impl Drop for Capturer {
	/// Manually delete the `dxgi_manager` and uninit the dxgi stuff inited on creation.
	fn drop(&mut self) {
		unsafe {
			delete_dxgi_manager(self.dxgi_manager);
			uninit_dxgi();
		}
	}
}<commit_msg>Some documentation in capture.rs<commit_after>use config::Region;
use color::{RGB8, BGRA8};

use libc::{c_void,
	uint8_t,
	size_t};
use std::ptr;
use std::mem;

/// Enum of results returned by DXGCap
#[repr(C)]
#[allow(dead_code)]
enum DXGCaptureResult {
	Ok,
	// Could not duplicate output, access denied. Might be in protected fullscreen.
	AccessDenied,
	// Access to the duplicated output was lost. Likely, mode was changed e.g. window => full
	AccessLost,
	// Error when trying to refresh outputs after some failure.
	RefreshFailure,
	// AcquireNextFrame timed out.
	Timeout,
	// General/Unexpected failure
	Fail,
}

#[link(name = "DXGCap")]
extern {
	fn init();

	fn uninit();

	fn create_dxgi_manager() -> *mut c_void;

	fn delete_dxgi_manager(dxgi_manager: *mut c_void);

	fn set_timeout(dxgi_manager: *mut c_void, timeout: u32);

	fn set_capture_source(dxgi_manager: *mut c_void, cs: u16);

	fn get_capture_source(dxgi_manager: *mut c_void) -> u16;

	fn refresh_output(dxgi_manager: *mut c_void) -> bool;

	fn get_output_dimensions(dxgi_manager: *const c_void, width: *mut u32, height: *mut u32);

	// Returns DXGI status code, HRESULT
	fn get_frame_bytes(dxgi_manager: *mut c_void, o_size: *mut size_t,
		o_bytes: *mut *mut uint8_t) -> DXGCaptureResult;
}

/// Possible errors when capturing
#[allow(dead_code)]
pub enum CaptureError {
	// Could not duplicate output, access denied. Might be in protected fullscreen.
	AccessDenied,
	// Access to the duplicated output was lost. Likely, mode was changed e.g. window => full
	AccessLost,
	// Error when trying to refresh outputs after some failure.
	RefreshFailure,
	// AcquireNextFrame timed out.
	Timeout,
	// General/Unexpected failure
	Fail,
}
impl CaptureError {
	/// Try to represent the DXGCap capture result as a CaptureError
	fn from_dxgcapture_result(cr: DXGCaptureResult) -> Option<CaptureError> {
		use self::DXGCaptureResult as D;
		match cr {
			D::Ok => None,
			D::AccessDenied => Some(CaptureError::AccessDenied),
			D::AccessLost => Some(CaptureError::AccessLost),
			D::RefreshFailure => Some(CaptureError::RefreshFailure),
			D::Timeout => Some(CaptureError::Timeout),
			D::Fail => Some(CaptureError::Fail),
		}
	}
}

static DXGI_PIXEL_SIZE: u64 = 4; // BGRA8 => 4 bytes, DXGI default

/// Initiate windows stuff that DXGCap requires.
fn init_dxgi() {
	unsafe { init(); }
}
fn uninit_dxgi() {
	unsafe { uninit(); }
}

/// Representation of an image as a vector of BGRA8 pixels, coupled with image dimensions
#[derive(Clone)]
pub struct Image {
	width: u32, height: u32,
	pixels: Vec<BGRA8>,
}
impl Image {
	/// Construct a new, empty image
	fn new() -> Image {
		Image{ width: 0, height: 0, pixels: Vec::new() }
	}
}

/// An analyzer with a slot for an image to be analyzed.
///
/// `resize_width` and `resize_height` specify what resolution to use when analyzing the image.
/// Often this decides number of rows/cols to skip.
/// Analysis that may be done is such as calculating average color of a region of the image.
#[derive(Clone)]
pub struct ImageAnalyzer {
	image: Image,
	resize_width: u32, resize_height: u32,
}
impl ImageAnalyzer {
	/// Construct a new `ImageAnalyzer` with an empty image slotted and resize dimensions of 1
	pub fn new() -> ImageAnalyzer {
		ImageAnalyzer{ image: Image::new(), resize_width: 1, resize_height: 1 }
	}

	/// Swap the image slotted in the analyzer for a new one. Return the old one.
	pub fn swap_slotted(&mut self, new: Image) -> Image {
		mem::replace(&mut self.image, new)
	}

	/// Change the dimensions to work with when analyzing
	pub fn set_resize_dimensions(&mut self, (resize_width, resize_height): (u32, u32)) {
		self.resize_width = if resize_width == 0 {
			self.image.width
		} else {
			resize_width
		};
		self.resize_height = if resize_height == 0 {
			self.image.height
		} else {
			resize_height
		};
	}

	/// Calculate the average color for a region of slotted image given by `led`
	pub fn average_color(&self, led: &Region) -> RGB8 {
		if self.image.pixels.len() == 0 {
			RGB8{ r: 0, g: 0, b: 0 }
		} else {
			let (resize_width_ratio, resize_height_ratio) = (
				self.image.width as f32 / self.resize_width as f32,
				self.image.height as f32 / self.resize_height as f32);
			let (y1, y2, x1, x2) = (
				(led.vscan.minimum * self.resize_height as f32) as usize,
				(led.vscan.maximum * self.resize_height as f32) as usize,
				(led.hscan.minimum * self.resize_width as f32) as usize,
				(led.hscan.maximum * self.resize_width as f32) as usize);
			let (mut r_sum, mut g_sum, mut b_sum) = (0u64, 0u64, 0u64);
			for row in y1..y2 {
				for col in x1..x2 {
					let pixel = &self.image.pixels[(
						row as f32 * resize_height_ratio *
						self.image.width as f32 +
						col as f32 * resize_width_ratio) as usize];
					r_sum += pixel.r as u64;
					g_sum += pixel.g as u64;
					b_sum += pixel.b as u64;
				}
			}

			let n_of_pixels = ((x2 - x1) * (y2 - y1)) as u64;
			RGB8{r: (r_sum/n_of_pixels) as u8,
				g: (g_sum/n_of_pixels) as u8,
				b: (b_sum/n_of_pixels) as u8 }
		}
	}
}

/// A screen capturer for capturing the contents of a monitor.
///
/// Currently this is not much more than a ffi wrapper for the DXGCap dxgi manager
/// and, as such, does not support capturing many fullscreen games, especially directx ones.
pub struct Capturer {
	dxgi_manager: *mut c_void,
}
impl Capturer {
	/// Initialize DXGCap and construct a new `Capturer` with a new dxgi manager
	pub fn new() -> Capturer {
		init_dxgi();
		let manager = unsafe { create_dxgi_manager() };
		if manager.is_null() {
			uninit_dxgi();
			panic!("Unexpected null pointer when constructing Capturer.")
		} else {
			Capturer{ dxgi_manager: manager }
		}
	}

	/// Specify the amount of time to wait for new frame before returning.
	pub fn set_timeout(&mut self, timeout: u32) {
		unsafe { set_timeout(self.dxgi_manager, timeout) }
	}

	/// Specify which monitor to capture from.
	/// A value of `0` results in capture of primary monitor.
	/// In windows, value of `cs` corresponds to monitor IDs in `Display\Screen Resolution`
	#[allow(dead_code)]
	pub fn set_capture_source(&mut self, cs: u16) {
		unsafe { set_capture_source(self.dxgi_manager, cs) }
	}

	/// Return index of current capture source
	#[allow(dead_code)]
	pub fn get_capture_source(&mut self) -> u16 {
		unsafe { get_capture_source(self.dxgi_manager) }
	}

	/// Manually refresh dxgi output adapters. May be needed if access to desktop duplication
	/// is lost and DXGCap did not fix it automatically.
	pub fn refresh_output(&mut self) -> bool {
		unsafe { refresh_output(self.dxgi_manager) }
	}

	/// Get the display resolution of current capture source.
	pub fn get_display_resolution(&self) -> (u32, u32) {
		let (mut width, mut height) = (0, 0);
		unsafe { get_output_dimensions(self.dxgi_manager, &mut width, &mut height); }
		(width, height)
	}

	/// Capture a frame from the capture source. Convert and return captured bytes as an `Image`
	pub fn capture_frame(&mut self) -> Result<Image, CaptureError> {
		let mut shared_buf_size: size_t = 0;
		let mut shared_buf = ptr::null_mut::<u8>();

		let cr = unsafe{
			get_frame_bytes(self.dxgi_manager, &mut shared_buf_size, &mut shared_buf) };
		if let DXGCaptureResult::Ok = cr  {
			if shared_buf.is_null() {
				Err(CaptureError::Fail)
			} else {
				let n_pixels = (shared_buf_size / DXGI_PIXEL_SIZE) as usize;
				let mut pixel_buf: Vec<BGRA8> = Vec::with_capacity(n_pixels);

				unsafe {
					ptr::copy(pixel_buf.as_mut_ptr(),
						shared_buf as *const BGRA8,
						n_pixels);
					pixel_buf.set_len(n_pixels);
				}

				let (width, height) = self.get_display_resolution();

				Ok(Image{ width: width, height: height, pixels: pixel_buf })
			}
		} else {
			Err(CaptureError::from_dxgcapture_result(cr)
				.expect("DXGCaptureResult was not an error"))
		}
	}
}
impl Drop for Capturer {
	/// Manually delete the `dxgi_manager` and uninit the dxgi stuff inited on creation.
	fn drop(&mut self) {
		unsafe {
			delete_dxgi_manager(self.dxgi_manager);
			uninit_dxgi();
		}
	}
}<|endoftext|>


<commit_before>extern crate proc_macro;
extern crate syn;

#[macro_use]
extern crate quote;

use proc_macro::TokenStream;

#[proc_macro_derive(StructMap)]
pub fn struct_map_generator(input: TokenStream) -> TokenStream {
    let s = input.to_string();
    let ast = syn::parse_macro_input(&s).expect("Can't parse rust code to AST");
    let gen = impl_struct_map(&ast);
    gen.parse().expect("Can't generate implementation code")
}

fn impl_struct_map(ast: &syn::MacroInput) -> quote::Tokens {
    let fields = match ast.body {
        syn::Body::Struct(ref data) => data.fields(),
        syn::Body::Enum(_) => panic!("#[derive(StructMap)] can only be used with structs"),
    };
    let field_names = fields.iter()
                            .filter_map(|field| field.ident.as_ref())
                            .collect::<Vec<_>>();

    let to_camel = |iden: &&syn::Ident| -> Option<String> {
        let name = iden.as_ref();
        let it = name.split('_');
        let mut ss: Vec<String> = Vec::new();
        for word in it {
            let mut v: Vec<char> = word.chars().collect();
            v[0] = v[0].to_uppercase().next().unwrap();
            let s: String = v.into_iter().collect();
            ss.push(s);
        }
        Some(ss.into_iter().collect::<String>().to_owned())
    };
    let tmp_field_names = field_names.clone();
    let field_names_camel = tmp_field_names.iter()
                                       .filter_map(to_camel)
                                       .collect::<Vec<_>>();

    let name = &ast.ident;

    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

    let t = quote! {
        impl #impl_generics StructMap for #name #ty_generics #where_clause {
            fn from_hashmap(hm: &std::collections::HashMap<&str, &str>) -> #name {
                let unwrap_get = |x| hm.get(x).unwrap();
                #name {
                    #(#field_names: FromStr::from_str(unwrap_get(#field_names_camel)).unwrap()),*
                    ,..Default::default()
                }
            }
        }
    };

    println!("Debug: {}", t.as_str());

    t
}
<commit_msg>use default when a field name doesn't match<commit_after>extern crate proc_macro;
extern crate syn;

#[macro_use]
extern crate quote;

use proc_macro::TokenStream;

#[proc_macro_derive(StructMap)]
pub fn struct_map_generator(input: TokenStream) -> TokenStream {
    let s = input.to_string();
    let ast = syn::parse_macro_input(&s).expect("Can't parse rust code to AST");
    let gen = impl_struct_map(&ast);
    gen.parse().expect("Can't generate implementation code")
}

fn impl_struct_map(ast: &syn::MacroInput) -> quote::Tokens {
    let fields = match ast.body {
        syn::Body::Struct(ref data) => data.fields(),
        syn::Body::Enum(_) => panic!("#[derive(StructMap)] can only be used with structs"),
    };
    let field_names = fields.iter()
                            .filter_map(|field| field.ident.as_ref())
                            .collect::<Vec<_>>();

    let to_camel = |iden: &&syn::Ident| -> Option<String> {
        let name = iden.as_ref();
        let it = name.split('_');
        let mut ss: Vec<String> = Vec::new();
        for word in it {
            let mut v: Vec<char> = word.chars().collect();
            v[0] = v[0].to_uppercase().next().unwrap();
            let s: String = v.into_iter().collect();
            ss.push(s);
        }
        Some(ss.into_iter().collect::<String>().to_owned())
    };
    let field_names_camel = field_names.iter()
                                       .filter_map(to_camel)
                                       .collect::<Vec<_>>();

    let name = &ast.ident;

    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

    let t = quote! {
        impl #impl_generics StructMap for #name #ty_generics #where_clause {
            fn from_hashmap(hm: &std::collections::HashMap<&str, &str>) -> #name {
                let dummy = "";
                #name {
                    #(#field_names: FromStr::from_str(hm.get(#field_names_camel).unwrap_or(&dummy)).unwrap_or_default()),*
                    ,..Default::default()
                }
            }
        }
    };

    println!("Debug:\n{}", t.as_str());

    t
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017 Sreejith Krishnan R
 *
 * 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 super::typeface::TypeFace;
use super::rect::Rect;
use ::props::Directionality;

pub trait TextRuler {
    fn font_size(&self) -> f32;
    fn typeface(&self) -> &TypeFace;

    fn measure(&self, text: &str, dir: &Directionality) -> Rect;
    fn measure_char(&self, unicode: u32, dir: &Directionality) -> Rect;
    fn measure_glyph(&self, glyph_index: u32, dir: &Directionality) -> Rect;

    fn ascent(&self) -> f32;
    fn descent(&self) -> f32;
}

pub trait MathRuler : TextRuler {
    fn script_percent_scale_down(&self) -> f32;
    fn script_script_percent_scale_down(&self) -> f32;
    fn delimited_sub_formula_min_height(&self) -> f32;
    fn display_operator_min_height(&self) -> f32;
    fn math_leading(&self) -> f32;
    fn axis_height(&self) -> f32;
    fn accent_base_height(&self) -> f32;
    fn flattened_accent_base_height(&self) -> f32;
    fn subscript_shift_down(&self) -> f32;
    fn subscript_top_max(&self) -> f32;
    fn subscript_baseline_drop_min(&self) -> f32;
    fn superscript_shift_up(&self) -> f32;
    fn superscript_shift_up_cramped(&self) -> f32;
    fn superscript_bottom_min(&self) -> f32;
    fn superscript_baseline_drop_max(&self) -> f32;
    fn sub_superscript_gap_min(&self) -> f32;
    fn superscript_bottom_max_with_subscript(&self) -> f32;
    fn space_after_script(&self) -> f32;
    fn upper_limit_gap_min(&self) -> f32;
    fn upper_limit_baseline_rise_min(&self) -> f32;
    fn lower_limit_gap_min(&self) -> f32;
    fn lower_limit_baseline_drop_min(&self) -> f32;
    fn stack_top_shift_up(&self) -> f32;
    fn stack_top_display_style_shift_up(&self) -> f32;
    fn stack_bottom_shift_down(&self) -> f32;
    fn stack_bottom_display_style_shift_down(&self) -> f32;
    fn stack_gap_min(&self) -> f32;
    fn stack_display_style_gap_min(&self) -> f32;
    fn stretch_stack_top_shift_up(&self) -> f32;
    fn stretch_stack_bottom_shift_down(&self) -> f32;
    fn stretch_stack_gap_above_min(&self) -> f32;
    fn stretch_stack_gap_below_min(&self) -> f32;
    fn fraction_numerator_shift_up(&self) -> f32;
    fn fraction_numerator_display_style_shift_up(&self) -> f32;
    fn fraction_denominator_shift_down(&self) -> f32;
    fn fraction_denominator_display_style_shift_down(&self) -> f32;
    fn numerator_gap_min(&self) -> f32;
    fn fraction_num_display_style_gap_min(&self) -> f32;
    fn fraction_rule_thickness(&self) -> f32;
    fn fraction_denominator_gap_min(&self) -> f32;
    fn fraction_denominator_display_style_gap_min(&self) -> f32;
    fn skewed_fraction_horizontal_gap(&self) -> f32;
    fn skewed_fraction_vertical_gap(&self) -> f32;
    fn overbar_vertical_gap(&self) -> f32;
    fn overbar_rule_thickness(&self) -> f32;
    fn overbar_extra_ascender(&self) -> f32;
    fn underbar_vertical_gap(&self) -> f32;
    fn underbar_rule_thickness(&self) -> f32;
    fn underbar_extra_descender(&self) -> f32;
    fn radical_vertical_gap(&self) -> f32;
    fn radical_display_style_vertical_gap(&self) -> f32;
    fn radical_rule_thickness(&self) -> f32;
    fn radical_extra_ascender(&self) -> f32;
    fn radical_kern_before_degree(&self) -> f32;
    fn radical_kern_after_degree(&self) -> f32;
    fn radical_degree_bottom_raise_percent(&self) -> f32;
    fn minimum_connector_overlap(&self, direction: &GlyphConstructionDirection) -> f32;
    fn glyph_variants(&self, unicode: u32, direction: &GlyphConstructionDirection) -> Vec<GlyphVariant>;
    fn glyph_assembly(&self, unicode: u32, direction: &GlyphConstructionDirection) -> GlyphAssembly;
}

pub enum GlyphConstructionDirection {
    Horizontal,
    Vertical,
}

pub struct GlyphVariant {
    glyph_index: u32,
    advance: f32,
}

impl GlyphVariant {
    pub fn new(glyph_index: u32, advance: f32) -> GlyphVariant {
        GlyphVariant { glyph_index, advance }
    }

    pub fn glyph_index(&self) -> u32 {
        self.glyph_index
    }

    pub fn advance(&self) -> f32 {
        self.advance
    }
}

pub struct GlyphAssemblyPart {
    glyph_index: u32,
    start_connector_length: f32,
    end_connector_length: f32,
    full_advance: f32,
    is_extender: bool,
}

impl GlyphAssemblyPart {
    pub fn new(glyph_index: u32, start_connector_length: f32, end_connector_length: f32,
               full_advance: f32, is_extender: bool) -> GlyphAssemblyPart {
        GlyphAssemblyPart {
            glyph_index,
            start_connector_length,
            end_connector_length,
            full_advance,
            is_extender,
        }
    }

    pub fn glyph_index(&self) -> u32 {
        self.glyph_index
    }

    pub fn start_connector_length(&self) -> f32 {
        self.start_connector_length
    }

    pub fn end_connector_length(&self) -> f32 {
        self.end_connector_length
    }

    pub fn full_advance(&self) -> f32 {
        self.full_advance
    }

    pub fn is_extender(&self) -> bool {
        self.is_extender
    }
}

pub struct GlyphAssembly {
    parts: Vec<GlyphAssemblyPart>,
    italics_correction: f32,
}

impl GlyphAssembly {
    pub fn new(parts: Vec<GlyphAssemblyPart>, italics_correction: f32) -> GlyphAssembly {
        GlyphAssembly {
            parts,
            italics_correction
        }
    }

    pub fn empty() -> GlyphAssembly {
        GlyphAssembly {
            parts: Vec::new(),
            italics_correction: 0.0,
        }
    }

    pub fn parts(&self) -> &Vec<GlyphAssemblyPart> {
        &self.parts
    }

    pub fn italics_correction(&self) -> f32 {
        self.italics_correction
    }
}<commit_msg>Common derives<commit_after>/*
 * Copyright 2017 Sreejith Krishnan R
 *
 * 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 super::typeface::TypeFace;
use super::rect::Rect;
use ::props::Directionality;

pub trait TextRuler {
    fn font_size(&self) -> f32;
    fn typeface(&self) -> &TypeFace;

    fn measure(&self, text: &str, dir: &Directionality) -> Rect;
    fn measure_char(&self, unicode: u32, dir: &Directionality) -> Rect;
    fn measure_glyph(&self, glyph_index: u32, dir: &Directionality) -> Rect;

    fn ascent(&self) -> f32;
    fn descent(&self) -> f32;
}

pub trait MathRuler : TextRuler {
    fn script_percent_scale_down(&self) -> f32;
    fn script_script_percent_scale_down(&self) -> f32;
    fn delimited_sub_formula_min_height(&self) -> f32;
    fn display_operator_min_height(&self) -> f32;
    fn math_leading(&self) -> f32;
    fn axis_height(&self) -> f32;
    fn accent_base_height(&self) -> f32;
    fn flattened_accent_base_height(&self) -> f32;
    fn subscript_shift_down(&self) -> f32;
    fn subscript_top_max(&self) -> f32;
    fn subscript_baseline_drop_min(&self) -> f32;
    fn superscript_shift_up(&self) -> f32;
    fn superscript_shift_up_cramped(&self) -> f32;
    fn superscript_bottom_min(&self) -> f32;
    fn superscript_baseline_drop_max(&self) -> f32;
    fn sub_superscript_gap_min(&self) -> f32;
    fn superscript_bottom_max_with_subscript(&self) -> f32;
    fn space_after_script(&self) -> f32;
    fn upper_limit_gap_min(&self) -> f32;
    fn upper_limit_baseline_rise_min(&self) -> f32;
    fn lower_limit_gap_min(&self) -> f32;
    fn lower_limit_baseline_drop_min(&self) -> f32;
    fn stack_top_shift_up(&self) -> f32;
    fn stack_top_display_style_shift_up(&self) -> f32;
    fn stack_bottom_shift_down(&self) -> f32;
    fn stack_bottom_display_style_shift_down(&self) -> f32;
    fn stack_gap_min(&self) -> f32;
    fn stack_display_style_gap_min(&self) -> f32;
    fn stretch_stack_top_shift_up(&self) -> f32;
    fn stretch_stack_bottom_shift_down(&self) -> f32;
    fn stretch_stack_gap_above_min(&self) -> f32;
    fn stretch_stack_gap_below_min(&self) -> f32;
    fn fraction_numerator_shift_up(&self) -> f32;
    fn fraction_numerator_display_style_shift_up(&self) -> f32;
    fn fraction_denominator_shift_down(&self) -> f32;
    fn fraction_denominator_display_style_shift_down(&self) -> f32;
    fn numerator_gap_min(&self) -> f32;
    fn fraction_num_display_style_gap_min(&self) -> f32;
    fn fraction_rule_thickness(&self) -> f32;
    fn fraction_denominator_gap_min(&self) -> f32;
    fn fraction_denominator_display_style_gap_min(&self) -> f32;
    fn skewed_fraction_horizontal_gap(&self) -> f32;
    fn skewed_fraction_vertical_gap(&self) -> f32;
    fn overbar_vertical_gap(&self) -> f32;
    fn overbar_rule_thickness(&self) -> f32;
    fn overbar_extra_ascender(&self) -> f32;
    fn underbar_vertical_gap(&self) -> f32;
    fn underbar_rule_thickness(&self) -> f32;
    fn underbar_extra_descender(&self) -> f32;
    fn radical_vertical_gap(&self) -> f32;
    fn radical_display_style_vertical_gap(&self) -> f32;
    fn radical_rule_thickness(&self) -> f32;
    fn radical_extra_ascender(&self) -> f32;
    fn radical_kern_before_degree(&self) -> f32;
    fn radical_kern_after_degree(&self) -> f32;
    fn radical_degree_bottom_raise_percent(&self) -> f32;
    fn minimum_connector_overlap(&self, direction: &GlyphConstructionDirection) -> f32;
    fn glyph_variants(&self, unicode: u32, direction: &GlyphConstructionDirection) -> Vec<GlyphVariant>;
    fn glyph_assembly(&self, unicode: u32, direction: &GlyphConstructionDirection) -> GlyphAssembly;
}

#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub enum GlyphConstructionDirection {
    Horizontal,
    Vertical,
}

#[derive(Debug)]
pub struct GlyphVariant {
    glyph_index: u32,
    advance: f32,
}

impl GlyphVariant {
    pub fn new(glyph_index: u32, advance: f32) -> GlyphVariant {
        GlyphVariant { glyph_index, advance }
    }

    pub fn glyph_index(&self) -> u32 {
        self.glyph_index
    }

    pub fn advance(&self) -> f32 {
        self.advance
    }
}

#[derive(Debug)]
pub struct GlyphAssemblyPart {
    glyph_index: u32,
    start_connector_length: f32,
    end_connector_length: f32,
    full_advance: f32,
    is_extender: bool,
}

impl GlyphAssemblyPart {
    pub fn new(glyph_index: u32, start_connector_length: f32, end_connector_length: f32,
               full_advance: f32, is_extender: bool) -> GlyphAssemblyPart {
        GlyphAssemblyPart {
            glyph_index,
            start_connector_length,
            end_connector_length,
            full_advance,
            is_extender,
        }
    }

    pub fn glyph_index(&self) -> u32 {
        self.glyph_index
    }

    pub fn start_connector_length(&self) -> f32 {
        self.start_connector_length
    }

    pub fn end_connector_length(&self) -> f32 {
        self.end_connector_length
    }

    pub fn full_advance(&self) -> f32 {
        self.full_advance
    }

    pub fn is_extender(&self) -> bool {
        self.is_extender
    }
}

#[derive(Debug)]
pub struct GlyphAssembly {
    parts: Vec<GlyphAssemblyPart>,
    italics_correction: f32,
}

impl GlyphAssembly {
    pub fn new(parts: Vec<GlyphAssemblyPart>, italics_correction: f32) -> GlyphAssembly {
        GlyphAssembly {
            parts,
            italics_correction
        }
    }

    pub fn empty() -> GlyphAssembly {
        GlyphAssembly {
            parts: Vec::new(),
            italics_correction: 0.0,
        }
    }

    pub fn parts(&self) -> &Vec<GlyphAssemblyPart> {
        &self.parts
    }

    pub fn italics_correction(&self) -> f32 {
        self.italics_correction
    }
}<|endoftext|>


<commit_before>use std::io::Read;
use error::{Result, Error, ErrorKind};
use reader::FbxEvent;
use super::CommonState;

#[derive(Debug, Clone)]
pub struct BinaryParser {
    version: u32,
    end_offset_stack: Vec<u32>,
}

impl BinaryParser {
    pub fn new(version: u32) -> Self {
        BinaryParser {
            version: version,
            end_offset_stack: vec![],
        }
    }

    pub fn next<R: Read>(&mut self, reader: &mut R, common: &mut CommonState) -> Result<FbxEvent> {
        // Read a node record header.
        let node_record_header = try!(NodeRecordHeader::load(reader, &mut common.pos));
        self.end_offset_stack.push(node_record_header.end_offset);

        // Read properties.
        Err(Error::new(common.pos, ErrorKind::Unimplemented("Parser for Binary FBX format is not implemented yet".to_string())))
    }
}

#[derive(Debug, Copy, Clone)]
struct NodeRecordHeader {
    end_offset: u32,
    num_properties: u32,
    property_list_len: u32,
    name_len: u8,
}

impl NodeRecordHeader {
    pub fn load<R: Read>(reader: &mut R, pos: &mut u64) -> Result<Self> {
        let end_offset = try_read_le_u32!(*pos, reader);
        let num_properties = try_read_le_u32!(*pos, reader);
        let property_list_len = try_read_le_u32!(*pos, reader);
        let name_len = try_read_le_u8!(*pos, reader);
        Ok(NodeRecordHeader {
            end_offset: end_offset,
            num_properties: num_properties,
            property_list_len: property_list_len,
            name_len: name_len,
        })
    }

    /// Check whether the header indicates there are no more children.
    pub fn is_null_record(&self) -> bool {
        self.end_offset == 0
            && self.num_properties == 0
            && self.property_list_len == 0
            && self.name_len == 0
    }
}
<commit_msg>Fix `BinaryParser` treating null record header as node start<commit_after>use std::io::Read;
use error::{Result, Error, ErrorKind};
use reader::FbxEvent;
use super::CommonState;

#[derive(Debug, Clone)]
pub struct BinaryParser {
    version: u32,
    end_offset_stack: Vec<u32>,
}

impl BinaryParser {
    pub fn new(version: u32) -> Self {
        BinaryParser {
            version: version,
            end_offset_stack: vec![],
        }
    }

    pub fn next<R: Read>(&mut self, reader: &mut R, common: &mut CommonState) -> Result<FbxEvent> {
        // Read a node record header.
        let node_record_header = try!(NodeRecordHeader::load(reader, &mut common.pos));
        if node_record_header.is_null_record() {
            // End of a node.
            return if let Some(expected_pos) = self.end_offset_stack.pop() {
                if common.pos == expected_pos as u64 {
                    Ok(FbxEvent::EndFbx)
                } else {
                    // Data is collapsed (the node doesn't end at expected position).
                    Err(Error::new(common.pos, ErrorKind::DataError("Node does not end at expected position".to_string())))
                }
            } else {
                // Data is collapsed (an extra node end marker found).
                Err(Error::new(common.pos, ErrorKind::DataError("An extra node end marker found".to_string())))
            };
        } else {
            // Start of a node.
            self.end_offset_stack.push(node_record_header.end_offset);
        }

        // Read properties.

        Err(Error::new(common.pos, ErrorKind::Unimplemented("Parser for Binary FBX format is not implemented yet".to_string())))
    }
}

#[derive(Debug, Copy, Clone)]
struct NodeRecordHeader {
    end_offset: u32,
    num_properties: u32,
    property_list_len: u32,
    name_len: u8,
}

impl NodeRecordHeader {
    pub fn load<R: Read>(reader: &mut R, pos: &mut u64) -> Result<Self> {
        let end_offset = try_read_le_u32!(*pos, reader);
        let num_properties = try_read_le_u32!(*pos, reader);
        let property_list_len = try_read_le_u32!(*pos, reader);
        let name_len = try_read_le_u8!(*pos, reader);
        Ok(NodeRecordHeader {
            end_offset: end_offset,
            num_properties: num_properties,
            property_list_len: property_list_len,
            name_len: name_len,
        })
    }

    /// Check whether the header indicates there are no more children.
    pub fn is_null_record(&self) -> bool {
        self.end_offset == 0
            && self.num_properties == 0
            && self.property_list_len == 0
            && self.name_len == 0
    }
}
<|endoftext|>


<commit_before>use std::thread;
use std::sync::Arc;

use glutin::{
    GlProfile,
    GlRequest,
    ContextBuilder,
    dpi::LogicalSize,
    window::WindowBuilder,
    event::{
        Event as GlutinEvent,
        WindowEvent,
        DeviceEvent,
        KeyboardInput,
        VirtualKeyCode,
        ElementState,
    },
    event_loop::{ControlFlow, EventLoop, EventLoopProxy},
};
use tokio::{
    sync::Mutex,
    runtime::{Runtime, Handle},
};

use crate::ipc_protocol::ServerConnection;

use super::{
    RequestRedraw,
    app::App,
    renderer::{
        Renderer,
        display_list::DisplayList,
    },
};

/// Run the renderer process in the current thread
///
/// This function must run in the main thread ONLY
pub fn main() {
    assert_main_thread();

    // The runtime for driving async code
    let runtime = Runtime::new()
        .expect("unable to spawn tokio runtime to run turtle server process");

    // The state of the drawing and the state/drawings associated with each turtle
    let app = Arc::new(App::default());
    // All of the drawing primitives in the order in which they wil be drawn
    //
    // This is managed separately from the rest of the app state because the display list is shared
    // among pretty much everything and so critical sections containing the display list need to be
    // as short as possible.
    let display_list = Arc::new(Mutex::new(DisplayList::default()));

    let event_loop = EventLoop::with_user_event();

    // Spawn the actual server thread(s) that will handle incoming IPC messages and asynchronous
    // update the shared state
    let event_loop_proxy = event_loop.create_proxy();
    spawn_async_server(runtime.handle().clone(), app.clone(), display_list.clone(), event_loop_proxy);

    let window_builder = {
        let drawing = runtime.handle().block_on(app.drawing_mut());
        WindowBuilder::new()
            .with_title(&drawing.title)
            .with_inner_size(LogicalSize {width: drawing.width, height: drawing.height})
    };

    // Create an OpenGL 3.x context for Pathfinder to use
    let gl_context = ContextBuilder::new()
        .with_gl(GlRequest::Latest)
        .with_gl_profile(GlProfile::Core)
        .with_vsync(true)
        .with_double_buffer(Some(true))
        .build_windowed(window_builder, &event_loop)
        .expect("bug: either could not create window or could not build OpenGL context");

    // Load OpenGL, and make the context current
    let gl_context = unsafe { gl_context.make_current().unwrap() };
    gl::load_with(|name| gl_context.get_proc_address(name) as *const _);

    // Need to draw using the physical size in pixels, not the logical size
    let draw_size = gl_context.window().inner_size();
    let mut renderer = Renderer::new(draw_size);

    event_loop.run(move |event, _, control_flow| match event {
        // Quit if the window is closed or if Esc is pressed and then released
        GlutinEvent::WindowEvent {
            event: WindowEvent::CloseRequested,
            ..
        } | GlutinEvent::WindowEvent {
            event: WindowEvent::Destroyed,
            ..
        } | GlutinEvent::DeviceEvent {
            event: DeviceEvent::Key(KeyboardInput {
                state: ElementState::Released,
                virtual_keycode: Some(VirtualKeyCode::Escape),
                ..
            }),
            ..
        } => {
            *control_flow = ControlFlow::Exit;
        },

        GlutinEvent::WindowEvent {
            event: WindowEvent::ScaleFactorChanged {scale_factor, ..},
            ..
        } => {
            renderer.set_scale_factor(scale_factor);
            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::WindowEvent {window_id, event} => {
            //TODO: Check if event modifies state and then send event
        },
        GlutinEvent::DeviceEvent {device_id, event} => {
            //TODO: Check if event modifies state and then send event
        },

        GlutinEvent::UserEvent(RequestRedraw) => {
            gl_context.window().request_redraw();
        },

        GlutinEvent::RedrawRequested(_) => {
            let drawing = {
                // Hold the drawing lock for as little time as possible
                let drawing = runtime.handle().block_on(app.drawing_mut());
                drawing.clone()
            };
            let display_list = runtime.handle().block_on(display_list.lock());

            let draw_size = gl_context.window().inner_size();
            renderer.render(draw_size, &display_list, &drawing);
            gl_context.swap_buffers().expect("unable to swap the buffer (for double buffering)");

            *control_flow = ControlFlow::Wait;
        },

        _ => {},
    });
}

fn assert_main_thread() {
    // This check isn't foolproof. Someone can always create a thread named "main".
    if thread::current().name().unwrap_or("") != "main" {
        // In order to maintain compatibility with MacOS, we need to make sure that windows are
        // only created on the main thread. We do this check on all platforms so that no one
        // can accidentally make a change that creates the window off of the main thread.
        unreachable!("bug: windows can only be created on the main thread");
    }
}

fn spawn_async_server(
    handle: Handle,
    app: Arc<App>,
    display_list: Arc<Mutex<DisplayList>>,
    event_loop: EventLoopProxy<RequestRedraw>,
) {
    thread::spawn(move || {
        // Spawn root task
        handle.spawn(async {
            let conn = ServerConnection::connect_stdin().await
                .expect("unable to establish turtle server connection");
            super::serve(conn, app, display_list, event_loop).await;
        });
    });
}
<commit_msg>Don't need wrapper thread to use spawn<commit_after>use std::thread;
use std::sync::Arc;

use glutin::{
    GlProfile,
    GlRequest,
    ContextBuilder,
    dpi::LogicalSize,
    window::WindowBuilder,
    event::{
        Event as GlutinEvent,
        WindowEvent,
        DeviceEvent,
        KeyboardInput,
        VirtualKeyCode,
        ElementState,
    },
    event_loop::{ControlFlow, EventLoop, EventLoopProxy},
};
use tokio::{
    sync::Mutex,
    runtime::{Runtime, Handle},
};

use crate::ipc_protocol::ServerConnection;

use super::{
    RequestRedraw,
    app::App,
    renderer::{
        Renderer,
        display_list::DisplayList,
    },
};

/// Run the renderer process in the current thread
///
/// This function must run in the main thread ONLY
pub fn main() {
    assert_main_thread();

    // The runtime for driving async code
    let runtime = Runtime::new()
        .expect("unable to spawn tokio runtime to run turtle server process");

    // The state of the drawing and the state/drawings associated with each turtle
    let app = Arc::new(App::default());
    // All of the drawing primitives in the order in which they wil be drawn
    //
    // This is managed separately from the rest of the app state because the display list is shared
    // among pretty much everything and so critical sections containing the display list need to be
    // as short as possible.
    let display_list = Arc::new(Mutex::new(DisplayList::default()));

    let event_loop = EventLoop::with_user_event();

    // Spawn the actual server thread(s) that will handle incoming IPC messages and asynchronous
    // update the shared state
    let event_loop_proxy = event_loop.create_proxy();
    spawn_async_server(runtime.handle().clone(), app.clone(), display_list.clone(), event_loop_proxy);

    let window_builder = {
        let drawing = runtime.handle().block_on(app.drawing_mut());
        WindowBuilder::new()
            .with_title(&drawing.title)
            .with_inner_size(LogicalSize {width: drawing.width, height: drawing.height})
    };

    // Create an OpenGL 3.x context for Pathfinder to use
    let gl_context = ContextBuilder::new()
        .with_gl(GlRequest::Latest)
        .with_gl_profile(GlProfile::Core)
        .with_vsync(true)
        .with_double_buffer(Some(true))
        .build_windowed(window_builder, &event_loop)
        .expect("bug: either could not create window or could not build OpenGL context");

    // Load OpenGL, and make the context current
    let gl_context = unsafe { gl_context.make_current().unwrap() };
    gl::load_with(|name| gl_context.get_proc_address(name) as *const _);

    // Need to draw using the physical size in pixels, not the logical size
    let draw_size = gl_context.window().inner_size();
    let mut renderer = Renderer::new(draw_size);

    event_loop.run(move |event, _, control_flow| match event {
        // Quit if the window is closed or if Esc is pressed and then released
        GlutinEvent::WindowEvent {
            event: WindowEvent::CloseRequested,
            ..
        } | GlutinEvent::WindowEvent {
            event: WindowEvent::Destroyed,
            ..
        } | GlutinEvent::DeviceEvent {
            event: DeviceEvent::Key(KeyboardInput {
                state: ElementState::Released,
                virtual_keycode: Some(VirtualKeyCode::Escape),
                ..
            }),
            ..
        } => {
            *control_flow = ControlFlow::Exit;
        },

        GlutinEvent::WindowEvent {
            event: WindowEvent::ScaleFactorChanged {scale_factor, ..},
            ..
        } => {
            renderer.set_scale_factor(scale_factor);
            //TODO: No idea if this next line is necessary or not
            gl_context.window().request_redraw();
        },

        GlutinEvent::WindowEvent {window_id, event} => {
            //TODO: Check if event modifies state and then send event
        },
        GlutinEvent::DeviceEvent {device_id, event} => {
            //TODO: Check if event modifies state and then send event
        },

        GlutinEvent::UserEvent(RequestRedraw) => {
            gl_context.window().request_redraw();
        },

        GlutinEvent::RedrawRequested(_) => {
            let drawing = {
                // Hold the drawing lock for as little time as possible
                let drawing = runtime.handle().block_on(app.drawing_mut());
                drawing.clone()
            };
            let display_list = runtime.handle().block_on(display_list.lock());

            let draw_size = gl_context.window().inner_size();
            renderer.render(draw_size, &display_list, &drawing);
            gl_context.swap_buffers().expect("unable to swap the buffer (for double buffering)");

            *control_flow = ControlFlow::Wait;
        },

        _ => {},
    });
}

fn assert_main_thread() {
    // This check isn't foolproof. Someone can always create a thread named "main".
    if thread::current().name().unwrap_or("") != "main" {
        // In order to maintain compatibility with MacOS, we need to make sure that windows are
        // only created on the main thread. We do this check on all platforms so that no one
        // can accidentally make a change that creates the window off of the main thread.
        unreachable!("bug: windows can only be created on the main thread");
    }
}

fn spawn_async_server(
    handle: Handle,
    app: Arc<App>,
    display_list: Arc<Mutex<DisplayList>>,
    event_loop: EventLoopProxy<RequestRedraw>,
) {
    // Spawn root task
    handle.spawn(async {
        let conn = ServerConnection::connect_stdin().await
            .expect("unable to establish turtle server connection");
        super::serve(conn, app, display_list, event_loop).await;
    });
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-test

pub fn main() {
    match 22i {
        0 .. 3 => {} //~ ERROR expected `=>`, found `..`
        _ => {}
    }
}
<commit_msg>rollup merge of #17702 : nick29581/enable-dots-test<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

pub fn main() {
    match 22i {
        0 .. 3 => {} //~ ERROR expected `=>`, found `..`
        _ => {}
    }
}
<|endoftext|>


<commit_before>use std::io::{Read, Result as IoResult};

#[derive(Debug)]
pub struct PeekReader<R: Read> {
    pub inner: R,
    pub peeked: Option<u8>,
}

impl<R: Read> Read for PeekReader<R> {

    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {

        if buf.is_empty() {
            return Ok(0);
        }

        if let Some(b) = self.peeked {

            buf[0] = b;
            self.peeked = None;
            self.inner.read(&mut buf[1..]).map(|x| x+1)
        }
        else {
            self.inner.read(buf)
        }
    }
}

impl<R: Read> PeekReader<R> {

    pub fn new(inner: R) -> PeekReader<R> {
        PeekReader {
            inner,
            peeked: None
        }
    }

    pub fn is_empty(&mut self) -> IoResult<bool> {

        if self.peeked.is_some() {
            return Ok(false);
        }

        let mut buf = [0; 1];
        let nb_read = self.read(&mut buf)?;

        Ok(
            match nb_read {
                0 => true,
                1 => {
                    self.peeked = Some(buf[0]);
                    false
                },
                _ => unreachable!(),
            }
        )
    }
}<commit_msg>Add impl Seek for PeekReader.<commit_after>use std::io::{Read, Result as IoResult};
use std::io::Seek;
use std::io::SeekFrom;

#[derive(Debug)]
pub struct PeekReader<R: Read> {
    pub inner: R,
    pub peeked: Option<u8>,
}

impl<R: Read> Read for PeekReader<R> {

    fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {

        if buf.is_empty() {
            return Ok(0);
        }

        if let Some(b) = self.peeked {

            buf[0] = b;
            self.peeked = None;
            self.inner.read(&mut buf[1..]).map(|x| x+1)
        }
        else {
            self.inner.read(buf)
        }
    }
}

impl<R: Read + Seek> Seek for PeekReader<R> {
    fn seek(&mut self, pos: SeekFrom) -> ::std::io::Result<u64> {
        if let Some(_) = self.peeked {
            self.inner.seek(SeekFrom::Current(-1))?;
            self.peeked = None;
        }
        self.inner.seek(pos)
    }
}

impl<R: Read> PeekReader<R> {

    pub fn new(inner: R) -> PeekReader<R> {
        PeekReader {
            inner,
            peeked: None
        }
    }

    pub fn is_empty(&mut self) -> IoResult<bool> {

        if self.peeked.is_some() {
            return Ok(false);
        }

        let mut buf = [0; 1];
        let nb_read = self.read(&mut buf)?;

        Ok(
            match nb_read {
                0 => true,
                1 => {
                    self.peeked = Some(buf[0]);
                    false
                },
                _ => unreachable!(),
            }
        )
    }
}<|endoftext|>


<commit_before>
use std::marker::PhantomData;

use Construct;
use Count;
use Data;
use ToPos;
use ToIndex;

/// Dimension is natural number, position is a list of numbers.
pub struct Permutation<T>(PhantomData<T>);

impl<T> Construct for Permutation<T> {
    fn new() -> Permutation<T> {
        Permutation(PhantomData)
    }
}

impl Count<usize> for Permutation<Data> {
    fn count(&self, dim: usize) -> usize {
        let mut res = 1;
        for x in 1..dim + 1 {
            res *= x;
        }
        res
    }
}

impl<'a> ToIndex<usize, &'a [usize]> for Permutation<Data> {
    fn to_index(&self, dim: usize, pos: &'a [usize]) -> usize {
        let mut index = 0;
        let mut count = self.count(dim);
        for (i, &x) in pos.iter().enumerate() {
            count /= dim - i;
            let lower = pos[..i].iter().filter(|&&y| y < x).count();
            index += count * (x - lower);
        }
        index
    }
}

impl<'a> ToPos<usize, &'a mut Vec<usize>> for Permutation<Data> {
    fn to_pos(&self, dim: usize, mut index: usize, pos: &'a mut Vec<usize>) {
        unsafe { pos.set_len(0); }

        let mut count = 1;
        for (j, x) in (1..dim + 1).enumerate() {
            count *= x;
            pos.push(j);
        }

        for i in 0..dim {
            let block = count / (dim - i);
            let ind = index / block;
            let item = pos.remove(ind);
            pos.push(item);
            count /= dim - i;
            index -= ind * block;
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use Construct;
    use Data;
    use Count;
    use ToPos;
    use ToIndex;

    #[test]
    fn test() {
        let permutation: Permutation<Data> = Construct::new();
        assert_eq!(permutation.count(1), 1);
        assert_eq!(permutation.count(2), 2);
        assert_eq!(permutation.count(3), 6);
        assert_eq!(permutation.count(4), 24);

        let mut pos = Vec::new();
        let dim = 4;
        let count = permutation.count(dim);
        for i in 0..count {
            permutation.to_pos(dim, i, &mut pos);
            let index = permutation.to_index(dim, &pos);
            assert_eq!(index, i);
        }
    }
}
<commit_msg>Going backward to eliminate `self.count`<commit_after>
use std::marker::PhantomData;

use Construct;
use Count;
use Data;
use ToPos;
use ToIndex;

/// Dimension is natural number, position is a list of numbers.
pub struct Permutation<T>(PhantomData<T>);

impl<T> Construct for Permutation<T> {
    fn new() -> Permutation<T> {
        Permutation(PhantomData)
    }
}

impl Count<usize> for Permutation<Data> {
    fn count(&self, dim: usize) -> usize {
        let mut res = 1;
        for x in 1..dim + 1 {
            res *= x;
        }
        res
    }
}

impl<'a> ToIndex<usize, &'a [usize]> for Permutation<Data> {
    fn to_index(&self, dim: usize, pos: &'a [usize]) -> usize {
        let mut index = 0;
        let mut count = 1;
        for (i, &x) in pos.iter().enumerate().rev() {
            let lower = pos[..i].iter().filter(|&&y| y < x).count();
            index += count * (x - lower);
            count *= dim - i;
        }
        index
    }
}

impl<'a> ToPos<usize, &'a mut Vec<usize>> for Permutation<Data> {
    fn to_pos(&self, dim: usize, mut index: usize, pos: &'a mut Vec<usize>) {
        unsafe { pos.set_len(0); }

        let mut count = 1;
        for (j, x) in (1..dim + 1).enumerate() {
            count *= x;
            pos.push(j);
        }

        for i in 0..dim {
            let block = count / (dim - i);
            let ind = index / block;
            let item = pos.remove(ind);
            pos.push(item);
            count /= dim - i;
            index -= ind * block;
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use Construct;
    use Data;
    use Count;
    use ToPos;
    use ToIndex;

    #[test]
    fn test() {
        let permutation: Permutation<Data> = Construct::new();
        assert_eq!(permutation.count(1), 1);
        assert_eq!(permutation.count(2), 2);
        assert_eq!(permutation.count(3), 6);
        assert_eq!(permutation.count(4), 24);

        let mut pos = Vec::new();
        let dim = 4;
        let count = permutation.count(dim);
        for i in 0..count {
            permutation.to_pos(dim, i, &mut pos);
            let index = permutation.to_index(dim, &pos);
            assert_eq!(index, i);
        }
    }
}
<|endoftext|>


<commit_before>//! Generates Rust code from IR.

#![cfg_attr(rustfmt, rustfmt::skip::macros(write_gen))]

use crate::ir::cc::RsIr;
use crate::ir::rs;
use gen_macro::{snippet, write_gen, CodeWriter, Gen, Snippet};
use itertools::Itertools;
use proc_macro2::{Ident, Punct, Spacing, Span, TokenStream};
use quote::{ToTokens, TokenStreamExt};
use std::{
    borrow::Borrow,
    fmt,
    io::{self, Write},
};

pub fn perform_codegen(
    db: &impl RsIr,
    mdl: &rs::Module,
    out: &mut impl io::Write,
) -> io::Result<()> {
    let mut out = CodeWriter::new(out);
    for item in &mdl.items {
        match item {
            rs::ItemKind::Struct(st) => st.lookup(db).codegen(db, &mut out)?,
        }
    }
    Ok(())
}

trait Codegen {
    fn codegen(&self, db: &impl RsIr, f: &mut CodeWriter) -> io::Result<()>;
}

impl Codegen for rs::Visibility {
    fn codegen(&self, _db: &impl RsIr, f: &mut CodeWriter) -> io::Result<()> {
        Ok(match self {
            rs::Visibility::Public => write!(f, "pub ")?,
            rs::Visibility::Private => (),
        })
    }
}

impl Codegen for rs::Struct {
    fn codegen(&self, db: &impl RsIr, f: &mut CodeWriter) -> io::Result<()> {
        writeln!(f, "#[repr(C, align({}))]", self.align)?;
        self.vis.codegen(db, f)?;
        writeln!(f, "struct {} {{", self.name)?;
        f.with_indent(|f| -> io::Result<()> {
            for field in &self.fields {
                field.vis.codegen(db, f)?;
                write!(f, "{}: ", field.name)?;
                field.ty(db).codegen(db, f)?;
                writeln!(f, ",")?;
            }
            Ok(())
        })?;
        writeln!(f, "}}")?;

        for method in &self.methods {
            gen_method(db, f, self, method)?;
        }

        Ok(())
    }
}

#[rustfmt::skip::macros(write_gen)]
fn gen_method(
    db: &impl RsIr,
    f: &mut CodeWriter,
    st: &rs::Struct,
    meth: &rs::Method,
) -> io::Result<()> {
    // TODO handle visibility (don't generate bindings for private methods)
    // TODO handle constness
    let func = meth.func();
    debug_assert_eq!(func.param_tys.len(), func.param_names.len());

    let arg_names = arg_names(meth);
    let args_sig: Snippet = arg_names
        .iter()
        .zip(meth.param_tys(db).with_db(db))
        .map(|(name, ty)| format!("{}: {}", name, ty))
        .join(", ")
        .into();

    let func_name = &func.name;
    let struct_name = &st.name;
    let trait_name = snippet!(db, "${struct_name}_${func_name}_Ext");

    // Create an extension trait for our method.
    write_gen!(f, db, "
        trait $trait_name {
            fn $func_name(self, $args_sig);
        }
    ")?;

    // impl the extension trait for NonNull<Struct>.
    write_gen!(f, db, "
        impl $trait_name for ::core::ptr::NonNull<$struct_name> {
            fn $func_name(self, $args_sig) {
                todo!()
            }
        }
    ")?;

    // Create a convenience wrapper for &mut self.
    let arg_names: Snippet = arg_names.iter().join(", ").into();
    write_gen!(f, db, "
        impl $struct_name {
            fn $func_name(&mut self, $args_sig) {
                ::core::ptr::NonNull::from(self).$func_name($arg_names)
            }
        }
    ")?;

    Ok(())
}

fn arg_names(meth: &rs::Method) -> Vec<rs::Ident> {
    meth.func()
        .param_names
        .iter()
        .enumerate()
        .map(|(idx, name)| name.clone().unwrap_or_else(|| format!("__{}", idx).into()))
        .collect()
}

/// Convenience trait to hide lifetime param.
trait DbRef: Copy {
    type DB: RsIr;
    fn get(&self) -> &Self::DB;
}
impl<DB: RsIr> DbRef for &'_ DB {
    type DB = DB;
    fn get(&self) -> &DB {
        &*self
    }
}

/// Used to attach a database ref to a value, so it can be used inside `quote!`.
trait WithDB: Sized {
    fn with_db<DB: DbRef>(self, db: DB) -> Context<DB, Self>;
}
impl<T> WithDB for T {
    fn with_db<DB: DbRef>(self, db: DB) -> Context<DB, Self> {
        Context { db, inner: self }
    }
}

struct Context<DB: DbRef, T> {
    db: DB,
    inner: T,
}

impl<DB: DbRef, T> Iterator for Context<DB, T>
where
    T: Iterator,
{
    type Item = Context<DB, T::Item>;
    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next().map(|v| v.with_db(self.db))
    }
}

trait ToTokensDb {
    fn to_tokens_db(&self, db: &impl RsIr, ts: &mut TokenStream);
}
impl<T: ToTokensDb> ToTokensDb for &'_ T {
    fn to_tokens_db(&self, db: &impl RsIr, ts: &mut TokenStream) {
        (*self).to_tokens_db(db, ts)
    }
}

impl<'a, DB: DbRef, T: ToTokensDb> ToTokens for Context<DB, T> {
    fn to_tokens(&self, ts: &mut TokenStream) {
        self.inner.borrow().to_tokens_db(self.db.get(), ts)
    }
}
impl<'a, DB: DbRef, T: ToTokensDb> fmt::Display for Context<DB, T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut ts = TokenStream::new();
        self.inner.borrow().to_tokens_db(self.db.get(), &mut ts);
        write!(f, "{}", ts)
    }
}

struct DoubleColon;
impl ToTokens for DoubleColon {
    fn to_tokens(&self, ts: &mut TokenStream) {
        ts.append(Punct::new(':', Spacing::Joint));
        ts.append(Punct::new(':', Spacing::Alone));
    }
}

impl ToTokens for rs::Ident {
    fn to_tokens(&self, ts: &mut TokenStream) {
        ts.append(Ident::new(self.as_str(), Span::call_site()));
    }
}
impl ToTokens for rs::Path {
    fn to_tokens(&self, ts: &mut TokenStream) {
        ts.append_separated(self.iter(), DoubleColon)
    }
}

impl<DB> Gen<DB> for rs::Ident {
    fn gen(&self, _db: &DB, f: &mut CodeWriter<'_>) -> io::Result<()> {
        write!(f, "{}", self.as_str())
    }
}
impl<DB> Gen<DB> for rs::Path {
    fn gen(&self, _db: &DB, f: &mut CodeWriter<'_>) -> io::Result<()> {
        for token in self.iter().map(rs::Ident::as_str).intersperse("::") {
            write!(f, "{}", token)?;
        }
        Ok(())
    }
}

impl ToTokensDb for rs::Ty {
    fn to_tokens_db(&self, db: &impl RsIr, ts: &mut TokenStream) {
        use rs::Ty::*;
        let name = match self {
            Error => "{error}",
            U8 => "u8",
            I8 => "i8",
            U16 => "u16",
            I16 => "i16",
            U32 => "u32",
            I32 => "i32",
            U64 => "u64",
            I64 => "i64",
            USize => "usize",
            ISize => "isize",
            F32 => "f32",
            F64 => "f64",
            Bool => "bool",
            Struct(id) => return id.lookup(db).name.to_tokens(ts),
        };
        ts.append(Ident::new(name, Span::call_site()));
    }
}
impl ToTokensDb for rs::Function {
    fn to_tokens_db(&self, _db: &impl RsIr, _ts: &mut TokenStream) {
        todo!()
    }
}

impl Codegen for rs::Ty {
    fn codegen(&self, db: &impl RsIr, f: &mut CodeWriter) -> io::Result<()> {
        let mut ts = TokenStream::new();
        self.with_db(db).to_tokens(&mut ts);
        write!(f, "{}", ts)
    }
}

impl<DB: RsIr> Gen<DB> for rs::Ty {
    fn gen(&self, db: &DB, f: &mut CodeWriter<'_>) -> io::Result<()> {
        let mut ts = TokenStream::new();
        self.with_db(db).to_tokens(&mut ts);
        write!(f, "{}", ts)
    }
}

#[cfg(test)]
mod tests {
    use crate::Session;

    #[test]
    fn pod() {
        let mut sess = Session::new();
        cpp_to_rs!(sess, {
            struct Pod {
                int a, b;
                char e;
                double c, d;
            };

            namespace rust_export {
              using ::Pod;
            }
        } => r#"
            #[repr(C, align(8))]
            pub struct Pod {
                pub a: i32,
                pub b: i32,
                pub e: i8,
                pub c: f64,
                pub d: f64,
            }
        "#);
    }

    #[test]
    fn nested_struct() {
        let mut sess = Session::new();
        cpp_to_rs!(sess, {
            struct Foo {
                int a, b;
            };
            struct Bar {
                char c, d;
                Foo foo;
            };
            namespace rust_export {
                using ::Bar;
            }
        } => r#"
            #[repr(C, align(4))]
            pub struct Bar {
                pub c: i8,
                pub d: i8,
                foo: Foo,
            }
            #[repr(C, align(4))]
            struct Foo {
                pub a: i32,
                pub b: i32,
            }
        "#);
    }

    #[test]
    fn method() {
        let mut sess = Session::new();
        cpp_to_rs!(sess, {
            struct Foo {
                int a, b;
                int sum(int c, int) const;
            };
            namespace rust_export {
                using ::Foo;
            }
        } => r#"
        #[repr(C, align(4))]
        pub struct Foo {
            pub a: i32,
            pub b: i32,
        }
        trait Foo_sum_Ext {
            fn sum(self, c: i32, __1: i32);
        }
        impl Foo_sum_Ext for ::core::ptr::NonNull<Foo> {
            fn sum(self, c: i32, __1: i32) {
                todo!()
            }
        }
        impl Foo {
            fn sum(&mut self, c: i32, __1: i32) {
                ::core::ptr::NonNull::from(self).sum(c, __1)
            }
        }
        "#);
    }

    // TODO handle these.
    #[test]
    #[should_panic(expected = "unsupported type")]
    fn nested_struct_in_namespace() {
        let mut sess = Session::new();
        cpp_to_rs!(sess, {
            namespace ns {
                struct Foo {
                    int a, b;
                };
            }
            struct Bar {
                char c, d;
                ns::Foo foo;
            };
            namespace rust_export {
                using ::Bar;
            }
        } => r#"
            #[repr(C, align(4))]
            struct Foo {
                pub a: i32,
                pub b: i32,
            }
            #[repr(C, align(4))]
            pub struct Bar {
                pub c: i8,
                pub d: i8,
                foo: Foo,
            }
        "#);
    }

    #[test]
    fn export_from_namespace() {
        let mut sess = Session::new();
        cpp_to_rs!(sess, {
            namespace ns {
                struct Foo {
                    int a, b;
                };
            }
            namespace rust_export {
                using ::ns::Foo;
            }
        } => r#"
            #[repr(C, align(4))]
            pub struct Foo {
                pub a: i32,
                pub b: i32,
            }
        "#);
    }

    #[test]
    fn namespace_in_export() {
        // Not supported yet.
        let mut sess = Session::new();
        cpp_lower!(sess, {
            struct Foo {
                int a, b;
            };
            namespace rust_export {
                namespace ns {
                    using ::Foo;
                }
            }
        } => [
            "invalid rust_export item"
        ]);
    }
}
<commit_msg>Use Gen trait instead of quote everywhere<commit_after>//! Generates Rust code from IR.

#![cfg_attr(rustfmt, rustfmt::skip::macros(write_gen))]

use crate::ir::cc::RsIr;
use crate::ir::rs;
use gen_macro::{snippet, write_gen, CodeWriter, Gen, Snippet};
use itertools::Itertools;
use std::io::{self, Write};

pub fn perform_codegen(
    db: &impl RsIr,
    mdl: &rs::Module,
    out: &mut impl io::Write,
) -> io::Result<()> {
    let mut out = CodeWriter::new(out);
    for item in &mdl.items {
        match item {
            rs::ItemKind::Struct(st) => st.lookup(db).gen(db, &mut out)?,
        }
    }
    Ok(())
}

impl<DB: RsIr> Gen<DB> for rs::Visibility {
    fn gen(&self, _db: &DB, f: &mut CodeWriter) -> io::Result<()> {
        Ok(match self {
            rs::Visibility::Public => write!(f, "pub ")?,
            rs::Visibility::Private => (),
        })
    }
}

impl<DB: RsIr> Gen<DB> for rs::Struct {
    fn gen(&self, db: &DB, f: &mut CodeWriter) -> io::Result<()> {
        writeln!(f, "#[repr(C, align({}))]", self.align)?;
        self.vis.gen(db, f)?;
        writeln!(f, "struct {} {{", self.name)?;
        f.with_indent(|f| -> io::Result<()> {
            for field in &self.fields {
                field.vis.gen(db, f)?;
                write!(f, "{}: ", field.name)?;
                field.ty(db).gen(db, f)?;
                writeln!(f, ",")?;
            }
            Ok(())
        })?;
        writeln!(f, "}}")?;

        for method in &self.methods {
            gen_method(db, f, self, method)?;
        }

        Ok(())
    }
}

#[rustfmt::skip::macros(write_gen)]
fn gen_method(
    db: &impl RsIr,
    f: &mut CodeWriter,
    st: &rs::Struct,
    meth: &rs::Method,
) -> io::Result<()> {
    // TODO handle visibility (don't generate bindings for private methods)
    // TODO handle constness
    let func = meth.func();
    debug_assert_eq!(func.param_tys.len(), func.param_names.len());

    let arg_names = arg_names(meth);
    let args_sig: Snippet = arg_names
        .iter()
        .zip(meth.param_tys(db))
        .map(|(name, ty)| snippet!(db, "$name: $ty").to_string())
        .join(", ")
        .into();

    let func_name = &func.name;
    let struct_name = &st.name;
    let trait_name = snippet!(db, "${struct_name}_${func_name}_Ext");

    // Create an extension trait for our method.
    write_gen!(f, db, "
        trait $trait_name {
            fn $func_name(self, $args_sig);
        }
    ")?;

    // impl the extension trait for NonNull<Struct>.
    write_gen!(f, db, "
        impl $trait_name for ::core::ptr::NonNull<$struct_name> {
            fn $func_name(self, $args_sig) {
                todo!()
            }
        }
    ")?;

    // Create a convenience wrapper for &mut self.
    let arg_names: Snippet = arg_names.iter().join(", ").into();
    write_gen!(f, db, "
        impl $struct_name {
            fn $func_name(&mut self, $args_sig) {
                ::core::ptr::NonNull::from(self).$func_name($arg_names)
            }
        }
    ")?;

    Ok(())
}

fn arg_names(meth: &rs::Method) -> Vec<rs::Ident> {
    meth.func()
        .param_names
        .iter()
        .enumerate()
        .map(|(idx, name)| name.clone().unwrap_or_else(|| format!("__{}", idx).into()))
        .collect()
}

/// Convenience trait to hide lifetime param.
trait DbRef: Copy {
    type DB: RsIr;
    fn get(&self) -> &Self::DB;
}
impl<DB: RsIr> DbRef for &'_ DB {
    type DB = DB;
    fn get(&self) -> &DB {
        &*self
    }
}

impl<DB> Gen<DB> for rs::Ident {
    fn gen(&self, _db: &DB, f: &mut CodeWriter<'_>) -> io::Result<()> {
        write!(f, "{}", self.as_str())
    }
}
impl<DB> Gen<DB> for rs::Path {
    fn gen(&self, _db: &DB, f: &mut CodeWriter<'_>) -> io::Result<()> {
        for token in self.iter().map(rs::Ident::as_str).intersperse("::") {
            write!(f, "{}", token)?;
        }
        Ok(())
    }
}

impl<DB: RsIr> Gen<DB> for rs::Ty {
    fn gen(&self, db: &DB, f: &mut CodeWriter<'_>) -> io::Result<()> {
        use rs::Ty::*;
        let name = match self {
            Error => "{error}",
            U8 => "u8",
            I8 => "i8",
            U16 => "u16",
            I16 => "i16",
            U32 => "u32",
            I32 => "i32",
            U64 => "u64",
            I64 => "i64",
            USize => "usize",
            ISize => "isize",
            F32 => "f32",
            F64 => "f64",
            Bool => "bool",
            Struct(id) => return id.lookup(db).name.gen(db, f),
        };
        write!(f, "{}", name)
    }
}

#[cfg(test)]
mod tests {
    use crate::Session;

    #[test]
    fn pod() {
        let mut sess = Session::new();
        cpp_to_rs!(sess, {
            struct Pod {
                int a, b;
                char e;
                double c, d;
            };

            namespace rust_export {
              using ::Pod;
            }
        } => r#"
            #[repr(C, align(8))]
            pub struct Pod {
                pub a: i32,
                pub b: i32,
                pub e: i8,
                pub c: f64,
                pub d: f64,
            }
        "#);
    }

    #[test]
    fn nested_struct() {
        let mut sess = Session::new();
        cpp_to_rs!(sess, {
            struct Foo {
                int a, b;
            };
            struct Bar {
                char c, d;
                Foo foo;
            };
            namespace rust_export {
                using ::Bar;
            }
        } => r#"
            #[repr(C, align(4))]
            pub struct Bar {
                pub c: i8,
                pub d: i8,
                foo: Foo,
            }
            #[repr(C, align(4))]
            struct Foo {
                pub a: i32,
                pub b: i32,
            }
        "#);
    }

    #[test]
    fn method() {
        let mut sess = Session::new();
        cpp_to_rs!(sess, {
            struct Foo {
                int a, b;
                int sum(int c, int) const;
            };
            namespace rust_export {
                using ::Foo;
            }
        } => r#"
        #[repr(C, align(4))]
        pub struct Foo {
            pub a: i32,
            pub b: i32,
        }
        trait Foo_sum_Ext {
            fn sum(self, c: i32, __1: i32);
        }
        impl Foo_sum_Ext for ::core::ptr::NonNull<Foo> {
            fn sum(self, c: i32, __1: i32) {
                todo!()
            }
        }
        impl Foo {
            fn sum(&mut self, c: i32, __1: i32) {
                ::core::ptr::NonNull::from(self).sum(c, __1)
            }
        }
        "#);
    }

    // TODO handle these.
    #[test]
    #[should_panic(expected = "unsupported type")]
    fn nested_struct_in_namespace() {
        let mut sess = Session::new();
        cpp_to_rs!(sess, {
            namespace ns {
                struct Foo {
                    int a, b;
                };
            }
            struct Bar {
                char c, d;
                ns::Foo foo;
            };
            namespace rust_export {
                using ::Bar;
            }
        } => r#"
            #[repr(C, align(4))]
            struct Foo {
                pub a: i32,
                pub b: i32,
            }
            #[repr(C, align(4))]
            pub struct Bar {
                pub c: i8,
                pub d: i8,
                foo: Foo,
            }
        "#);
    }

    #[test]
    fn export_from_namespace() {
        let mut sess = Session::new();
        cpp_to_rs!(sess, {
            namespace ns {
                struct Foo {
                    int a, b;
                };
            }
            namespace rust_export {
                using ::ns::Foo;
            }
        } => r#"
            #[repr(C, align(4))]
            pub struct Foo {
                pub a: i32,
                pub b: i32,
            }
        "#);
    }

    #[test]
    fn namespace_in_export() {
        // Not supported yet.
        let mut sess = Session::new();
        cpp_lower!(sess, {
            struct Foo {
                int a, b;
            };
            namespace rust_export {
                namespace ns {
                    using ::Foo;
                }
            }
        } => [
            "invalid rust_export item"
        ]);
    }
}
<|endoftext|>