<commit_before>//! A module to assist with fetching test data from the crate's resources.
use std::path::PathBuf;

use super::util;

#[derive(Copy, Clone)]
pub enum Rsrc {
    /// A defanged 64-bit version of kernel32.dll.
    K32,
    /// from: https://www.bigmessowires.com/2015/10/08/a-handmade-executable-file/
    /// since it doesn't have any sections or optional header, good for testing
    /// corner cases.
    TINY,
    /// from: https://joenord.com/apps/nop/
    NOP,
    /// from: https://github.com/gentilkiwi/mimikatz/releases/tag/2.2.0-20190512
    MIMI,
}

/// Fetch the file system name of the given resource.
///
/// ```
/// use lancelot::rsrc::*;
/// assert_eq!(get_name(Rsrc::K32), "k32.bin");
/// ```
pub fn get_name(rsrc: Rsrc) -> String {
    match rsrc {
        Rsrc::K32 => String::from("k32.bin"),
        Rsrc::TINY => String::from("tiny.exe"),
        Rsrc::NOP => String::from("nop.exe"),
        Rsrc::MIMI => String::from("mimikatz.exe_"),
    }
}

/// Fetch the file system path of the given resource.
///
/// ```
/// use lancelot::rsrc::*;
/// assert!(get_path(Rsrc::K32).ends_with("k32.bin"));
/// ```
pub fn get_path(rsrc: Rsrc) -> String {
    let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    d.push("resources");
    d.push("test");
    d.push(get_name(rsrc));
    String::from(d.to_str().unwrap())
}

/// Fetch the contents of the given resource.
///
/// ```
/// use lancelot::rsrc::*;
/// assert_eq!(get_buf(Rsrc::K32)[0], b'M');
/// assert_eq!(get_buf(Rsrc::K32)[1], b'Z');
/// ```
pub fn get_buf(rsrc: Rsrc) -> Vec<u8> {
    let path = get_path(rsrc);
    let mut buf = util::read_file(&path).unwrap();
    match rsrc {
        Rsrc::K32 => {
            buf[0] = b'M';
            buf[1] = b'Z';
        }
        Rsrc::TINY => {
            // pass
        }
        Rsrc::NOP => {
            // pass
        }
        Rsrc::MIMI => {
            // pass
        }
    }
    buf
}
<commit_msg>rsrc: move doctests to tests<commit_after>//! A module to assist with fetching test data from the crate's resources.
use std::path::PathBuf;

use super::util;

#[derive(Copy, Clone)]
pub enum Rsrc {
    /// A defanged 64-bit version of kernel32.dll.
    K32,
    /// from: https://www.bigmessowires.com/2015/10/08/a-handmade-executable-file/
    /// since it doesn't have any sections or optional header, good for testing
    /// corner cases.
    TINY,
    /// from: https://joenord.com/apps/nop/
    NOP,
    /// from: https://github.com/gentilkiwi/mimikatz/releases/tag/2.2.0-20190512
    MIMI,
}

/// Fetch the file system name of the given resource.
pub fn get_name(rsrc: Rsrc) -> String {
    match rsrc {
        Rsrc::K32 => String::from("k32.bin"),
        Rsrc::TINY => String::from("tiny.exe"),
        Rsrc::NOP => String::from("nop.exe"),
        Rsrc::MIMI => String::from("mimikatz.exe_"),
    }
}

/// Fetch the file system path of the given resource.
pub fn get_path(rsrc: Rsrc) -> String {
    let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    d.push("resources");
    d.push("test");
    d.push(get_name(rsrc));
    String::from(d.to_str().unwrap())
}

/// Fetch the contents of the given resource.
pub fn get_buf(rsrc: Rsrc) -> Vec<u8> {
    let path = get_path(rsrc);
    let mut buf = util::read_file(&path).unwrap();
    match rsrc {
        Rsrc::K32 => {
            buf[0] = b'M';
            buf[1] = b'Z';
        }
        Rsrc::TINY => {
            // pass
        }
        Rsrc::NOP => {
            // pass
        }
        Rsrc::MIMI => {
            // pass
        }
    }
    buf
}

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

    #[test]
    fn test_get_buf() {
        assert_eq!(get_buf(Rsrc::K32)[0], b'M');
        assert_eq!(get_buf(Rsrc::K32)[1], b'Z');
    }

    #[test]
    fn test_get_path() {
        assert!(get_path(Rsrc::K32).ends_with("k32.bin"));
    }

    #[test]
    fn test_get_name() {
        assert_eq!(get_name(Rsrc::K32), "k32.bin");
    }
}
<|endoftext|>


<commit_before><commit_msg>Update note regading XP compatability<commit_after><|endoftext|>


<commit_before><commit_msg>Can add method to class builder<commit_after><|endoftext|>


<commit_before><commit_msg>Fix completion with a clap update<commit_after><|endoftext|>


<commit_before><commit_msg>The receiver can be moved, since we never use it again<commit_after><|endoftext|>


<commit_before>use winapi::um::processenv::GetStdHandle;
use winapi::um::winbase::STD_OUTPUT_HANDLE;
use winapi::um::wincon::GetConsoleScreenBufferInfo;
use winapi::um::wincon::{CONSOLE_SCREEN_BUFFER_INFO, COORD, SMALL_RECT};

/// Query the current processes's output, returning its width and height as a
/// number of characters.
///
/// # Errors
///
/// Returns `None` if the output isn't to a terminal.
///
/// # Example
///
/// To get the dimensions of your terminal window, simply use the following:
///
/// ```no_run
/// # use term_size;
/// if let Some((w, h)) = term_size::dimensions() {
///     println!("Width: {}\nHeight: {}", w, h);
/// } else {
///     println!("Unable to get term size :(")
/// }
/// ```
pub fn dimensions() -> Option<(usize, usize)> {
    let null_coord = COORD { X: 0, Y: 0 };
    let null_smallrect = SMALL_RECT {
        Left: 0,
        Top: 0,
        Right: 0,
        Bottom: 0,
    };

    let stdout_h = unsafe { GetStdHandle(STD_OUTPUT_HANDLE) };
    let mut console_data = CONSOLE_SCREEN_BUFFER_INFO {
        dwSize: null_coord,
        dwCursorPosition: null_coord,
        wAttributes: 0,
        srWindow: null_smallrect,
        dwMaximumWindowSize: null_coord,
    };

    if unsafe { GetConsoleScreenBufferInfo(stdout_h, &mut console_data) } != 0 {
        Some(((console_data.srWindow.Right - console_data.srWindow.Left) as usize,
                (console_data.srWindow.Bottom - console_data.srWindow.Top) as usize))
    } else {
        None
    }
}

/// Query the current processes's output, returning its width and height as a
/// number of characters. Returns `None` if the output isn't to a terminal.
///
/// # Errors
///
/// Returns `None` if the output isn't to a terminal.
///
/// # Example
///
/// To get the dimensions of your terminal window, simply use the following:
///
/// ```no_run
/// # use term_size;
/// if let Some((w, h)) = term_size::dimensions() {
///     println!("Width: {}\nHeight: {}", w, h);
/// } else {
///     println!("Unable to get term size :(")
/// }
/// ```
pub fn dimensions_stdout() -> Option<(usize, usize)> { dimensions() }

/// This isn't implemented for Windows
///
/// # Panics
///
/// This function `panic!`s unconditionally with the `unimplemented!`
/// macro
pub fn dimensions_stdin() -> Option<(usize, usize)> { unimplemented!() }

/// This isn't implemented for Windows
///
/// # Panics
///
/// This function `panic!`s unconditionally with the `unimplemented!`
/// macro
pub fn dimensions_stderr() -> Option<(usize, usize)> { unimplemented!() }
<commit_msg>Fix off-by-one error in Windows screen calculation<commit_after>use winapi::um::processenv::GetStdHandle;
use winapi::um::winbase::STD_OUTPUT_HANDLE;
use winapi::um::wincon::GetConsoleScreenBufferInfo;
use winapi::um::wincon::{CONSOLE_SCREEN_BUFFER_INFO, COORD, SMALL_RECT};

/// Query the current processes's output, returning its width and height as a
/// number of characters.
///
/// # Errors
///
/// Returns `None` if the output isn't to a terminal.
///
/// # Example
///
/// To get the dimensions of your terminal window, simply use the following:
///
/// ```no_run
/// # use term_size;
/// if let Some((w, h)) = term_size::dimensions() {
///     println!("Width: {}\nHeight: {}", w, h);
/// } else {
///     println!("Unable to get term size :(")
/// }
/// ```
pub fn dimensions() -> Option<(usize, usize)> {
    let null_coord = COORD { X: 0, Y: 0 };
    let null_smallrect = SMALL_RECT {
        Left: 0,
        Top: 0,
        Right: 0,
        Bottom: 0,
    };

    let stdout_h = unsafe { GetStdHandle(STD_OUTPUT_HANDLE) };
    let mut console_data = CONSOLE_SCREEN_BUFFER_INFO {
        dwSize: null_coord,
        dwCursorPosition: null_coord,
        wAttributes: 0,
        srWindow: null_smallrect,
        dwMaximumWindowSize: null_coord,
    };

    if unsafe { GetConsoleScreenBufferInfo(stdout_h, &mut console_data) } != 0 {
        Some(((console_data.srWindow.Right - console_data.srWindow.Left + 1) as usize,
                (console_data.srWindow.Bottom - console_data.srWindow.Top + 1) as usize))
    } else {
        None
    }
}

/// Query the current processes's output, returning its width and height as a
/// number of characters. Returns `None` if the output isn't to a terminal.
///
/// # Errors
///
/// Returns `None` if the output isn't to a terminal.
///
/// # Example
///
/// To get the dimensions of your terminal window, simply use the following:
///
/// ```no_run
/// # use term_size;
/// if let Some((w, h)) = term_size::dimensions() {
///     println!("Width: {}\nHeight: {}", w, h);
/// } else {
///     println!("Unable to get term size :(")
/// }
/// ```
pub fn dimensions_stdout() -> Option<(usize, usize)> { dimensions() }

/// This isn't implemented for Windows
///
/// # Panics
///
/// This function `panic!`s unconditionally with the `unimplemented!`
/// macro
pub fn dimensions_stdin() -> Option<(usize, usize)> { unimplemented!() }

/// This isn't implemented for Windows
///
/// # Panics
///
/// This function `panic!`s unconditionally with the `unimplemented!`
/// macro
pub fn dimensions_stderr() -> Option<(usize, usize)> { unimplemented!() }
<|endoftext|>


<commit_before>use std::fs::File;
use std::ffi::OsStr;
use std::path::Path;
use std::io::prelude::*;
use std::io::Cursor;
use std::collections::HashSet;
use quick_xml::writer::Writer;
use quick_xml::events::{Event, BytesEnd, BytesStart};
use quick_xml::errors::Result;
use tracer::TracerData;
use config::Config;

fn write_header<T:Write>(writer: &mut Writer<T>, config: &Config) -> Result<usize> {

    writer.write_event(Event::Start(BytesStart::borrowed(b"sources", b"sources".len())))?;
    writer.write_event(Event::Start(BytesStart::borrowed(b"source", b"source".len())))?;

    let parent_folder = match config.manifest.parent() {
        Some(s) => s.to_str().unwrap_or_default(),
        None => "",
    };

    writer.write(parent_folder.as_bytes()).unwrap();
    writer.write_event(Event::End(BytesEnd::borrowed(b"source")))?;
    writer.write_event(Event::End(BytesEnd::borrowed(b"sources")))
}

/// Input only from single source file
fn write_class<T:Write>(writer: &mut Writer<T>, coverage: &[&TracerData]) ->Result<usize> {
    if !coverage.is_empty() {
        let covered = coverage.iter().filter(|&x| (x.hits > 0)).count();
        let covered = (covered as f32)/(coverage.len() as f32);
        let name = coverage[0].path.file_name().unwrap_or_default().to_str().unwrap_or_default();
        let mut class = BytesStart::owned(b"class".to_vec(), b"class".len());
        class.push_attribute(("filename", name));
        class.push_attribute(("line-rate", covered.to_string().as_ref()));

        writer.write_event(Event::Start(class))?;
        writer.write_event(Event::Start(BytesStart::borrowed(b"lines", b"lines".len())))?;
        for trace in coverage {
            let mut line = BytesStart::owned(b"line".to_vec(), b"line".len());
            line.push_attribute(("line", trace.line.to_string().as_ref()));
            line.push_attribute(("hits", trace.hits.to_string().as_ref()));
            writer.write_event(Event::Empty(line))?;
        }
        writer.write_event(Event::End(BytesEnd::borrowed(b"lines")))?;
        writer.write_event(Event::End(BytesEnd::borrowed(b"class")))
    } else {
        Ok(0)
    }
}

/// Input only tracer data from a single source folder
fn write_package<T:Write>(mut writer: &mut Writer<T>,
                          package: &str,
                          coverage: &[&TracerData]) -> Result<usize> {
    let covered = coverage.iter().filter(|&x| (x.hits > 0)).count();
    let covered = (covered as f32)/(coverage.len() as f32);

    let mut pack = BytesStart::owned(b"package".to_vec(), b"package".len());
    pack.push_attribute(("name", package));
    pack.push_attribute(("line-rate", covered.to_string().as_ref()));
    writer.write_event(Event::Start(pack))?;
    writer.write_event(Event::Start(BytesStart::borrowed(b"classes", b"classes".len())))?;
    let mut file_set: HashSet<&OsStr> = HashSet::new();

    for t in coverage.iter() {
        let filename = t.path.file_name();
        if !file_set.contains(filename.unwrap_or_default()) {
            file_set.insert(filename.unwrap_or_default());
            let class = coverage.iter()
                                .filter(|x| x.path.file_name() == filename)
                                .map(|x| *x)
                                .collect::<Vec<_>>();

            write_class(&mut writer, &class)?;
        }
    }

    writer.write_event(Event::End(BytesEnd::borrowed(b"classes")))?;
    writer.write_event(Event::End(BytesEnd::borrowed(b"package")))
}

pub fn export(coverage_data: &[TracerData], config: &Config) {
    let mut file = File::create("cobertura.xml").unwrap();
    let mut writer = Writer::new(Cursor::new(Vec::new()));
    // Construct cobertura xml
    let covered = coverage_data.iter().filter(|&x| (x.hits > 0 )).count();
    let total = coverage_data.len();
    let line_rate = (covered as f32)/(total as f32);
    let mut cov = BytesStart::owned(b"coverage".to_vec(), b"coverage".len());
    cov.push_attribute(("line-rate", line_rate.to_string().as_ref()));
    writer.write_event(Event::Start(cov)).unwrap();
    let _ = write_header(&mut writer, &config);
    // other data
    writer.write_event(Event::Start(BytesStart::borrowed(b"packages", b"packages".len()))).unwrap();

    let mut folder_set: HashSet<&Path> = HashSet::new();
    for t in coverage_data.iter() {
        let parent = match t.path.parent() {
            Some(x) => x,
            None => continue,
        };
        if !folder_set.contains(&parent) {
            folder_set.insert(parent);
            let package_name = match parent.strip_prefix(config.manifest.as_path()) {
                Ok(p) => p,
                _ => parent,
            };
            let package = coverage_data.iter().filter(|x| x.path.parent() == Some(&parent)).collect::<Vec<_>>();
            let _ = write_package(&mut writer, package_name.to_str().unwrap_or_default(), &package);
        }
    }

    writer.write_event(Event::End(BytesEnd::borrowed(b"packages"))).unwrap();
    writer.write_event(Event::End(BytesEnd::borrowed(b"coverage"))).unwrap();
    let result = writer.into_inner().into_inner();
    file.write_all(&result).unwrap();
}
<commit_msg>Added additional necessary details to cobertura reports.<commit_after>use std::fs::File;
use std::ffi::OsStr;
use std::path::Path;
use std::io::prelude::*;
use std::io::Cursor;
use std::collections::HashSet;
use quick_xml::writer::Writer;
use quick_xml::events::{Event, BytesEnd, BytesStart, BytesDecl};
use quick_xml::errors::Result;
use tracer::TracerData;
use config::Config;

fn write_header<T:Write>(writer: &mut Writer<T>, config: &Config) -> Result<usize> {

    writer.write_event(Event::Start(BytesStart::borrowed(b"sources", b"sources".len())))?;
    writer.write_event(Event::Start(BytesStart::borrowed(b"source", b"source".len())))?;

    let parent_folder = match config.manifest.parent() {
        Some(s) => s.to_str().unwrap_or_default(),
        None => "",
    };

    writer.write(parent_folder.as_bytes()).unwrap();
    writer.write_event(Event::End(BytesEnd::borrowed(b"source")))?;
    writer.write_event(Event::End(BytesEnd::borrowed(b"sources")))
}

/// Input only from single source file
fn write_class<T:Write>(writer: &mut Writer<T>, manifest_path: &Path, coverage: &[&TracerData]) ->Result<usize> {
    if !coverage.is_empty() {
        let covered = coverage.iter().filter(|&x| (x.hits > 0)).count();
        let covered = (covered as f32)/(coverage.len() as f32);
        //let filename = coverage[0].path.file_name().unwrap_or_default().to_str().unwrap_or_default();
        let filename = match coverage[0].path.strip_prefix(manifest_path) {
            Ok(p) => p,
            _ => coverage[0].path.as_path()
        };
        let name = coverage[0].path.file_stem().unwrap_or_default().to_str().unwrap_or_default();

        let mut class = BytesStart::owned(b"class".to_vec(), b"class".len());
        class.push_attribute(("name", name));
        class.push_attribute(("filename", filename.to_str().unwrap_or_default()));
        class.push_attribute(("line-rate", covered.to_string().as_ref()));
        class.push_attribute(("branch-rate", "1.0"));
        class.push_attribute(("complexity", "0.0"));
        writer.write_event(Event::Start(class))?;
        writer.write_event(Event::Empty(BytesStart::owned(b"methods".to_vec(), b"methods".len())))?;
        writer.write_event(Event::Start(BytesStart::borrowed(b"lines", b"lines".len())))?;
        for trace in coverage {
            let mut line = BytesStart::owned(b"line".to_vec(), b"line".len());
            line.push_attribute(("number", trace.line.to_string().as_ref()));
            line.push_attribute(("hits", trace.hits.to_string().as_ref()));
            writer.write_event(Event::Empty(line))?;
        }
        writer.write_event(Event::End(BytesEnd::borrowed(b"lines")))?;
        writer.write_event(Event::End(BytesEnd::borrowed(b"class")))
    } else {
        Ok(0)
    }
}

/// Input only tracer data from a single source folder
fn write_package<T:Write>(mut writer: &mut Writer<T>,
                          package: &Path,
                          package_name: &str,
                          coverage: &[&TracerData]) -> Result<usize> {
    let covered = coverage.iter().filter(|&x| (x.hits > 0)).count();
    let covered = (covered as f32)/(coverage.len() as f32);

    let mut pack = BytesStart::owned(b"package".to_vec(), b"package".len());
    pack.push_attribute(("name", package_name));
    pack.push_attribute(("line-rate", covered.to_string().as_ref()));
    pack.push_attribute(("branch-rate", "1.0"));
    pack.push_attribute(("complexity", "0.0"));
    writer.write_event(Event::Start(pack))?;
    writer.write_event(Event::Start(BytesStart::borrowed(b"classes", b"classes".len())))?;
    let mut file_set: HashSet<&OsStr> = HashSet::new();

    for t in coverage.iter() {
        let filename = t.path.file_name();
        if !file_set.contains(filename.unwrap_or_default()) {
            file_set.insert(filename.unwrap_or_default());
            let class = coverage.iter()
                                .filter(|x| x.path.file_name() == filename)
                                .map(|x| *x)
                                .collect::<Vec<_>>();

            write_class(&mut writer, package, &class)?;
        }
    }

    writer.write_event(Event::End(BytesEnd::borrowed(b"classes")))?;
    writer.write_event(Event::End(BytesEnd::borrowed(b"package")))
}

pub fn export(coverage_data: &[TracerData], config: &Config) {
    let mut file = File::create("cobertura.xml").unwrap();
    let mut writer = Writer::new(Cursor::new(Vec::new()));
    writer.write_event(Event::Decl(BytesDecl::new(b"1.0", None, None))).unwrap();
    // Construct cobertura xml
    let covered = coverage_data.iter().filter(|&x| (x.hits > 0 )).count();
    let total = coverage_data.len();
    let line_rate = (covered as f32)/(total as f32);
    let mut cov = BytesStart::owned(b"coverage".to_vec(), b"coverage".len());
    cov.push_attribute(("line-rate", line_rate.to_string().as_ref()));
    cov.push_attribute(("branch-rate", "1.0"));
    cov.push_attribute(("version", "1.9"));
    cov.push_attribute(("timestamp", "1502816840"));

    writer.write_event(Event::Start(cov)).unwrap();
    let _ = write_header(&mut writer, &config);
    // other data
    writer.write_event(Event::Start(BytesStart::borrowed(b"packages", b"packages".len()))).unwrap();

    let mut folder_set: HashSet<&Path> = HashSet::new();
    for t in coverage_data.iter() {
        let parent = match t.path.parent() {
            Some(x) => x,
            None => continue,
        };
        if !folder_set.contains(&parent) {
            folder_set.insert(parent);
            let manifest_path = config.manifest.parent().unwrap_or(&config.manifest);
            let package_name = match parent.strip_prefix(manifest_path) {
                Ok(p) => p,
                _ => manifest_path,
            };
            let package_name = package_name.to_str().unwrap_or_default();
            let package = coverage_data.iter().filter(|x| x.path.parent() == Some(&parent)).collect::<Vec<_>>();
            let _ = write_package(&mut writer, &manifest_path, package_name, &package);
        }
    }

    writer.write_event(Event::End(BytesEnd::borrowed(b"packages"))).unwrap();
    writer.write_event(Event::End(BytesEnd::borrowed(b"coverage"))).unwrap();
    let result = writer.into_inner().into_inner();
    file.write_all(&result).unwrap();
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
use self::rustc_serialize::json::Json;

use std::marker::PhantomData;

//
// Telemetry is a mechanism used to capture metrics in an application,
// and either store the data locally or upload to a server for
// statistical analysis.
//
// Examples of usage:
// - capturing the speed of an operation;
// - finding out if users are actually using a feature;
// - finding out how the duration of a session;
// - determine the operating system on which the application is executed;
// - determining the configuration of the application;
// - capturing the operations that slow down the application;
// - determining the amount of I/O performed by the application;
// - ...
//
// The abstraction used by this library is the Histogram. Each
// Histogram serves to capture a specific measurement, store it
// locally and/or upload it to the server. Several types of Histograms
// are provided, suited to distinct kinds of measures.
//
//
// Important note: the memory used by a histogram is recollected only
// when its instance of `telemetry` is garbage-collected. In other words,
// if a histogram goes out of scope for some reason, its data remains
// in telemetry and will be stored and/or uploaded in accordance with the
// configuration of this telemetry instance.
//

// FIXME: Attempting to use a histogram with a wrong instance of
// `telemetry` is a Bad Idea. Add an assertion.

//
// A software version, e.g. [2015, 10, 10, 0]
//
type Version = [u32;4];

//
// Metadata on a histogram.
//
struct Metadata {
    // A key used to identify the histogram. Must be unique to the instance
    // of `telemetry`.
    key: String,

    // Optionally, a version of the product at which this histogram expires.
    expires: Option<Version>,

    // A human-redable description of the histogram. Do not forget units
    // or explanations of enumeration labels.
    description: String,
}

trait Histogram<T> {
    fn record(&self, telemetry: &mut Telemetry, value: T) {
        self.record_cb(telemetry, || Some(value))
    }
    fn record_cb<F>(&self, telemetry: &mut Telemetry, _: F) where F: FnOnce() -> Option<T>;
}

pub trait Flatten<T> {
    fn as_u32(&self) -> u32;
}

impl Flatten<u32> for u32 {
    fn as_u32(&self) -> u32 {
        *self
    }
}

//
//
// Flag histograms.
//
// This histogram type allows you to record a single value. This type
// is useful if you need to track whether a feature was ever used
// during a session. You only need to add a single line of code which
// sets the flag when the feature is used because the histogram is
// initialized with a default value of false (flag not set).
//
//

struct FlagStorage {
    // `true` once we have called `record`, `false` until then.
    encountered: bool
}

impl FlagStorage {
    fn new() -> FlagStorage {
        FlagStorage {
            encountered: false
        }
    }
}

impl RawStorage for FlagStorage {
    fn store(&mut self, _: u32) {
        self.encountered = true;
    }
    fn serialize(&self) -> Json {
        unreachable!() // FIXME: Implement
    }
}

pub struct Flag {
    shared: Shared,
}

impl Histogram<()> for Flag {
    fn record_cb<F>(&self, telemetry: &mut Telemetry, cb: F) where F: FnOnce() -> Option<()>  {
        if !self.shared.should_record(telemetry) {
            return;
        }
        match cb() {
            None => {}
            Some(()) => telemetry.raw_record(&self.shared, 0)
        }
    }
}

impl Flag {
    fn new(telemetry: &mut Telemetry, meta: Metadata) -> Flag {
        let storage = Box::new(FlagStorage::new());
        let index = telemetry.register_storage(meta, storage).unwrap(); // FIXME: This will self-destruct if we have expired. We probably just want to neutralize the histogram.
        Flag {
            shared: Shared::new(index),
        }
    }
}

//
// Linear histograms.
//
//
// Linear histograms classify numeric integer values into same-sized
// buckets. This type is typically used for percentages.
//
struct LinearStorage {
    values: Vec<u32>// We cannot use an array here, as this would make the struct unsized
}

impl LinearStorage {
    fn new(buckets: usize) -> LinearStorage {
        LinearStorage {
            values: Vec::with_capacity(buckets)
        }
    }
}

impl RawStorage for LinearStorage {
    fn store(&mut self, index: u32) {
        self.values[index as usize] += 1;
    }
    fn serialize(&self) -> Json {
        unreachable!() // FIXME: Implement
    }
}

pub struct Linear<T> where T: Flatten<T> {
    witness: PhantomData<T>,
    shared: Shared,
    min: u32,
    max: u32, // Invariant: max > min
    buckets: u32 // FIXME: We assume that u32 <= usize. Assert?
}

impl<T> Histogram<T> for Linear<T> where T: Flatten<T> {
    fn record_cb<F>(&self, telemetry: &mut Telemetry, cb: F) where F: FnOnce() -> Option<T>  {
        if !self.shared.should_record(telemetry) {
            return;
        }
        match cb() {
            None => {}
            Some(v) => telemetry.raw_record(&self.shared, self.get_bucket(v))
        }
    }

}

impl<T> Linear<T> where T: Flatten<T> {
    fn new(telemetry: &mut Telemetry, meta: Metadata, min: u32, max: u32, buckets: usize) -> Linear<T> {
        let storage = Box::new(LinearStorage::new(buckets));
        let index = telemetry.register_storage(meta, storage).unwrap();  // FIXME: This will self-destruct if we have expired. We probably just want to neutralize the histogram.
        Linear {
            witness: PhantomData,
            shared: Shared::new(index),
            min: min,
            max: max,
            buckets: buckets as u32
        }
    }

    fn get_bucket(&self, value: T) -> u32 {
        let value = value.as_u32();
        if value >= self.max {
            0
        } else if value <= self.min {
            self.buckets - 1 as u32
        } else {
            let num = value as f32 - self.min as f32;
            let den = self.max as f32 - self.min as f32;
            let res = (num / den) * self.buckets as f32;
            res as u32
        }
    }
}

struct Telemetry {
    product: String,
    version: Version,
    is_active: bool,
    stores: Vec<Box<RawStorage>>,
}

struct StorageSettings; // FIXME: Define
struct ServerSettings; // FIXME: Define

impl Telemetry {
    pub fn new(product: String,
               version: Version,
               _: Option<StorageSettings>,
               _: Option<ServerSettings>) -> Telemetry {
        Telemetry {
            product: product,
            version: version,
            stores: Vec::new(),
            is_active: false,
        }
    }

    fn register_storage(&mut self, meta: Metadata, storage: Box<RawStorage>) -> Option<usize> {
        {
            // Don't add the histogram if it is expired.
            match meta.expires {
                Some(v) if v <= self.version => return None,
                _ => {}
            }
        }
        self.stores.push(storage);
        Some(self.stores.len())
    }
    fn raw_record(&mut self, histogram: &Shared, value: u32) {
        let ref mut storage = self.stores[histogram.index];
        storage.store(value);
    }
}

//
// Low-level, untyped, implementation of histogram storage.
//
trait RawStorage {
    fn store(&mut self, value: u32);
    fn serialize(&self) -> Json;
}

//
// Features shared by all histograms
//
struct Shared {
    // A key used to map a histogram to its storage owned by telemetry.
    index: usize,

    // `true` unless the histogram has been deactivated by user request.
    // If `false`, no data will be recorded for this histogram.
    is_active: bool,

    // `false` unless the histogram is designed to expire at some
    // version of the product and the current version is more recent.
    is_expired: bool
}

impl Shared {
    fn new(index: usize) -> Shared {
        Shared {
            index: index,
            is_active: true,
            is_expired: false
        }
    }

    fn should_record(&self, telemetry: &Telemetry) -> bool {
        if !self.is_active {
            return false;
        }
        if self.is_expired {
            return false;
        }
        if !telemetry.is_active {
            return false;
        }
        return true;
    }
}
<commit_msg>Added documentation and assertions.<commit_after>extern crate rustc_serialize;
use self::rustc_serialize::json::Json;

use std::marker::PhantomData;

//
// Telemetry is a mechanism used to capture metrics in an application,
// and either store the data locally or upload to a server for
// statistical analysis.
//
// Examples of usage:
// - capturing the speed of an operation;
// - finding out if users are actually using a feature;
// - finding out how the duration of a session;
// - determine the operating system on which the application is executed;
// - determining the configuration of the application;
// - capturing the operations that slow down the application;
// - determining the amount of I/O performed by the application;
// - ...
//
// The abstraction used by this library is the Histogram. Each
// Histogram serves to capture a specific measurement, store it
// locally and/or upload it to the server. Several types of Histograms
// are provided, suited to distinct kinds of measures.
//
//
// Memory note: the memory used by a histogram is recollected only
// when its instance of `telemetry` is garbage-collected. In other words,
// if a histogram goes out of scope for some reason, its data remains
// in telemetry and will be stored and/or uploaded in accordance with the
// configuration of this telemetry instance.
//

//
// A software version, e.g. [2015, 10, 10, 0]
//
type Version = [u32;4];

//
// Metadata on a histogram.
//
struct Metadata {
    // A key used to identify the histogram. Must be unique to the instance
    // of `telemetry`.
    key: String,

    // Optionally, a version of the product at which this histogram expires.
    expires: Option<Version>,

    // A human-redable description of the histogram. Do not forget units
    // or explanations of enumeration labels.
    description: String,
}

trait Histogram<T> {
    //
    // Record a value in this histogram.
    //
    // The value is recorded only if all of the following conditions are met:
    // - `telemetry` is activated; and
    // - this histogram has not expired; and
    // - the histogram is active.
    //
    fn record(&self, telemetry: &mut Telemetry, value: T) {
        self.record_cb(telemetry, || Some(value))
    }

    //
    // Record a value in this histogram, as provided by a callback.
    //
    // The callback is triggered only if all of the following conditions are met:
    // - `telemetry` is activated; and
    // - this histogram has not expired; and
    // - the histogram is active.
    //
    // If the callback returns `None`, no value is recorded.
    //
    fn record_cb<F>(&self, telemetry: &mut Telemetry, _: F) where F: FnOnce() -> Option<T>;
}

pub trait Flatten<T> {
    fn as_u32(&self) -> u32;
}

impl Flatten<u32> for u32 {
    fn as_u32(&self) -> u32 {
        *self
    }
}

//
//
// Flag histograms.
//
// This histogram type allows you to record a single value. This type
// is useful if you need to track whether a feature was ever used
// during a session. You only need to add a single line of code which
// sets the flag when the feature is used because the histogram is
// initialized with a default value of false (flag not set).
//
//

struct FlagStorage {
    // `true` once we have called `record`, `false` until then.
    encountered: bool
}

impl FlagStorage {
    fn new() -> FlagStorage {
        FlagStorage {
            encountered: false
        }
    }
}

impl RawStorage for FlagStorage {
    fn store(&mut self, _: u32) {
        self.encountered = true;
    }
    fn serialize(&self) -> Json {
        unreachable!() // FIXME: Implement
    }
}

pub struct Flag {
    shared: Shared,
}

impl Histogram<()> for Flag {
    fn record_cb<F>(&self, telemetry: &mut Telemetry, cb: F) where F: FnOnce() -> Option<()>  {
        if !self.shared.should_record(telemetry) {
            return;
        }
        match cb() {
            None => {}
            Some(()) => telemetry.raw_record(&self.shared, 0)
        }
    }
}

impl Flag {
    fn new(telemetry: &mut Telemetry, meta: Metadata) -> Flag {
        let storage = Box::new(FlagStorage::new());
        let key = telemetry.register_storage(meta, storage).unwrap(); // FIXME: This will self-destruct if we have expired. We probably just want to neutralize the histogram.
        Flag {
            shared: Shared::new(key),
        }
    }
}

//
// Linear histograms.
//
//
// Linear histograms classify numeric integer values into same-sized
// buckets. This type is typically used for percentages.
//
struct LinearStorage {
    values: Vec<u32>// We cannot use an array here, as this would make the struct unsized
}

impl LinearStorage {
    fn new(buckets: usize) -> LinearStorage {
        LinearStorage {
            values: Vec::with_capacity(buckets)
        }
    }
}

impl RawStorage for LinearStorage {
    fn store(&mut self, index: u32) {
        self.values[index as usize] += 1;
    }
    fn serialize(&self) -> Json {
        unreachable!() // FIXME: Implement
    }
}

pub struct Linear<T> where T: Flatten<T> {
    witness: PhantomData<T>,
    shared: Shared,
    min: u32,
    max: u32, // Invariant: max > min
    buckets: u32 // FIXME: We assume that u32 <= usize. Assert?
}

impl<T> Histogram<T> for Linear<T> where T: Flatten<T> {
    fn record_cb<F>(&self, telemetry: &mut Telemetry, cb: F) where F: FnOnce() -> Option<T>  {
        if !self.shared.should_record(telemetry) {
            return;
        }
        match cb() {
            None => {}
            Some(v) => telemetry.raw_record(&self.shared, self.get_bucket(v))
        }
    }

}

impl<T> Linear<T> where T: Flatten<T> {
    fn new(telemetry: &mut Telemetry, meta: Metadata, min: u32, max: u32, buckets: usize) -> Linear<T> {
        let storage = Box::new(LinearStorage::new(buckets));
        let key = telemetry.register_storage(meta, storage).unwrap();  // FIXME: This will self-destruct if we have expired. We probably just want to neutralize the histogram.
        Linear {
            witness: PhantomData,
            shared: Shared::new(key),
            min: min,
            max: max,
            buckets: buckets as u32
        }
    }

    fn get_bucket(&self, value: T) -> u32 {
        let value = value.as_u32();
        if value >= self.max {
            0
        } else if value <= self.min {
            self.buckets - 1 as u32
        } else {
            let num = value as f32 - self.min as f32;
            let den = self.max as f32 - self.min as f32;
            let res = (num / den) * self.buckets as f32;
            res as u32
        }
    }
}

struct Telemetry {
    // The name of the product.
    product: String,

    // The version of the product. Some histograms may be limited to
    // specific versions of the product.
    version: Version,

    // Has telemetry been activated? `false` by default.
    is_active: bool,

    // The storage for all histograms.
    stores: Vec<Box<RawStorage>>,
}

struct StorageSettings; // FIXME: Define
struct ServerSettings; // FIXME: Define

struct StorageKey {
    index: usize,
    telemetry: *const Telemetry
}

impl Telemetry {
    //
    // Construct a new instance of telemetry.
    //
    // This instance is deactivated by default.
    //
    pub fn new(product: String,
               version: Version,
               _: Option<StorageSettings>,
               _: Option<ServerSettings>) -> Telemetry {
        Telemetry {
            product: product,
            version: version,
            stores: Vec::new(),
            is_active: false,
        }
    }

    //
    // Activate/deactivate Telemetry. If Telemetry is deactivated,
    // it will not record new data.
    //
    pub fn set_active(&mut self, value: bool) {
        self.is_active = value;
    }

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

    //
    // Register the `RawStorage` used by an histogram.
    //
    fn register_storage(&mut self, meta: Metadata, storage: Box<RawStorage>) -> Option<StorageKey> {
        {
            // Don't add the histogram if it is expired.
            match meta.expires {
                Some(v) if v <= self.version => return None,
                _ => {}
            }
        }
        self.stores.push(storage);
        Some(StorageKey {
            index: self.stores.len(), // Note: this won't be 0.
            telemetry: self
        })
    }

    //
    //
    //
    fn raw_record(&mut self, histogram: &Shared, value: u32) {
        assert!(histogram.key.index != 0);
        assert!(histogram.key.telemetry == self);
        let ref mut storage = self.stores[histogram.key.index];
        storage.store(value);
    }
}

//
// Low-level, untyped, implementation of histogram storage.
//
trait RawStorage {
    fn store(&mut self, value: u32);
    fn serialize(&self) -> Json;
}

//
// Features shared by all histograms
//
struct Shared {
    // A key used to map a histogram to its storage owned by telemetry.
    key: StorageKey,

    // `true` unless the histogram has been deactivated by user request.
    // If `false`, no data will be recorded for this histogram.
    is_active: bool,

    // `false` unless the histogram is designed to expire at some
    // version of the product and the current version is more recent.
    is_expired: bool
}

impl Shared {
    fn new(key: StorageKey) -> Shared {
        Shared {
            key: key,
            is_active: true,
            is_expired: false
        }
    }

    fn should_record(&self, telemetry: &Telemetry) -> bool {
        if !self.is_active {
            return false;
        }
        if self.is_expired {
            return false;
        }
        if !telemetry.is_active {
            return false;
        }
        return true;
    }
}
<|endoftext|>


<commit_before>use ncurses::{attr_get, attr_t};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Color(pub u8);

impl ColorPair {
    pub fn from_term(colors: i16) -> ColorPair {
        ColorPair(colors as u8)
    }

    pub fn to_term(&self) -> i16 {
        match self {
            &ColorPair(colors) => colors as i16,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ColorPair(pub u8);

const BLACK : Color = Color(16);
const WHITE : Color = Color(231);

fn color_chanel_256_to_6(x: u8) -> (u8, u8) {
    match x {
        0x00...0x2F => (0, x - 0x00),
        0x2F...0x5F => (1, 0x5F - x),
        0x5F...0x73 => (1, x - 0x5F),
        0x73...0x87 => (2, 0x87 - x),
        0x87...0x9B => (2, x - 0x87),
        0x9B...0xAF => (3, 0xAF - x),
        0xAF...0xC3 => (3, x - 0xAF),
        0xC3...0xD7 => (4, 0xD7 - x),
        0xD7...0xEB => (4, x - 0xD7),
        0xEB...0xFF => (5, 0xFF - x),
        _ => unreachable!()
    }
}

impl Color {
    pub fn from_rgb256(r: u8, g: u8, b: u8) -> Color {
        // find color in color cube
        let (r6, r6diff) = color_chanel_256_to_6(r);
        let (g6, g6diff) = color_chanel_256_to_6(g);
        let (b6, b6diff) = color_chanel_256_to_6(b);
        let cube_diff = r6diff + g6diff + b6diff;
        // find color in grayscale palette
        let value = ((r as u16 + g as u16 + b as u16) / 3) as u8;
        let (gray_color, origin_value) = match value {
            0x00...0x04 => (BLACK, 0x00),
            0x04...0xF4 => {
                let index = (value - 0x04) / 10;
                (Color::grayscale(index), 0x08 + index * 0xA)
            }
            0xF4...0xFF => (WHITE, 0xff),
            _ => unreachable!(),
        };
        let gray_diff = if origin_value > r { origin_value - r } else { r - origin_value }
                      + if origin_value > g { origin_value - g } else { g - origin_value }
                      + if origin_value > b { origin_value - b } else { b - origin_value };
        // compare best match and return
        if gray_diff < cube_diff {
            gray_color
        } else {
            Color::color_cube(r6, g6, b6)
        }
    }

    fn color_cube(r: u8, g: u8, b: u8) -> Color {
        Color(16 + r * 36 + g * 6 + b)
    }

    pub fn grayscale(index: u8) -> Color {
        assert!(index <= 23);
        Color(232 + index)
    }

    pub fn to_term(&self) -> i16 {
        match self {
            &Color(color) => color as i16,
        }
    }
}

bitflags! {
    flags Attr: u64 {
        const NORMAL    = 0,
        const BOLD      = 1 << (8 + 13),
        const UNDERLINE = 1 << (8 +  9),
        // todo: add italic, invert and blink
    }
}

// impl Attr {
//     fn from_term(attrs: attr_t) -> Attr {
//         attrs as Attr
//     }

//     fn to_term(&self) -> attr_t {
//         *self as attr_t
//     }
// }

pub struct Style {
    pub colors: ColorPair,
    pub attrs: Attr
}

struct StyleContext {
    prev_style: Style
}

impl Attr {
    pub fn to_term(&self) -> u64 {
        self.bits
    }
}

impl Style {
    fn normal(colors: ColorPair) -> Style {
        Style {
            colors: colors,
            attrs: NORMAL
        }
    }

    pub fn current() -> Style {
        let mut attrs: attr_t = 0;
        let mut colors: i16 = 0;
        attr_get(&mut attrs, &mut colors);
        Style {
            colors: ColorPair::from_term(colors),
            attrs: Attr::from_bits(attrs).unwrap_or(NORMAL)
        }
    }

    // pub fn context(&self) -> StyleContext {
    //     self.set();
    //     StyleContext { prev_style: Style::current() }
    // }

}

// impl Drop for StyleContext {
//     fn drop(&mut self) {
//         self.prev_style.set();
//     }
// }
<commit_msg>fixed build on x32 systems<commit_after>use ncurses::{attr_get, attr_t};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Color(pub u8);

impl ColorPair {
    pub fn from_term(colors: i16) -> ColorPair {
        ColorPair(colors as u8)
    }

    pub fn to_term(&self) -> i16 {
        match self {
            &ColorPair(colors) => colors as i16,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ColorPair(pub u8);

const BLACK : Color = Color(16);
const WHITE : Color = Color(231);

fn color_chanel_256_to_6(x: u8) -> (u8, u8) {
    match x {
        0x00...0x2F => (0, x - 0x00),
        0x2F...0x5F => (1, 0x5F - x),
        0x5F...0x73 => (1, x - 0x5F),
        0x73...0x87 => (2, 0x87 - x),
        0x87...0x9B => (2, x - 0x87),
        0x9B...0xAF => (3, 0xAF - x),
        0xAF...0xC3 => (3, x - 0xAF),
        0xC3...0xD7 => (4, 0xD7 - x),
        0xD7...0xEB => (4, x - 0xD7),
        0xEB...0xFF => (5, 0xFF - x),
        _ => unreachable!()
    }
}

impl Color {
    pub fn from_rgb256(r: u8, g: u8, b: u8) -> Color {
        // find color in color cube
        let (r6, r6diff) = color_chanel_256_to_6(r);
        let (g6, g6diff) = color_chanel_256_to_6(g);
        let (b6, b6diff) = color_chanel_256_to_6(b);
        let cube_diff = r6diff + g6diff + b6diff;
        // find color in grayscale palette
        let value = ((r as u16 + g as u16 + b as u16) / 3) as u8;
        let (gray_color, origin_value) = match value {
            0x00...0x04 => (BLACK, 0x00),
            0x04...0xF4 => {
                let index = (value - 0x04) / 10;
                (Color::grayscale(index), 0x08 + index * 0xA)
            }
            0xF4...0xFF => (WHITE, 0xff),
            _ => unreachable!(),
        };
        let gray_diff = if origin_value > r { origin_value - r } else { r - origin_value }
                      + if origin_value > g { origin_value - g } else { g - origin_value }
                      + if origin_value > b { origin_value - b } else { b - origin_value };
        // compare best match and return
        if gray_diff < cube_diff {
            gray_color
        } else {
            Color::color_cube(r6, g6, b6)
        }
    }

    fn color_cube(r: u8, g: u8, b: u8) -> Color {
        Color(16 + r * 36 + g * 6 + b)
    }

    pub fn grayscale(index: u8) -> Color {
        assert!(index <= 23);
        Color(232 + index)
    }

    pub fn to_term(&self) -> i16 {
        match self {
            &Color(color) => color as i16,
        }
    }
}

bitflags! {
    flags Attr: attr_t {
        const NORMAL    = 0,
        const BOLD      = 1 << (8 + 13),
        const UNDERLINE = 1 << (8 +  9),
        // todo: add italic, invert and blink
    }
}

// impl Attr {
//     fn from_term(attrs: attr_t) -> Attr {
//         attrs as Attr
//     }

//     fn to_term(&self) -> attr_t {
//         *self as attr_t
//     }
// }

pub struct Style {
    pub colors: ColorPair,
    pub attrs: Attr
}

struct StyleContext {
    prev_style: Style
}

impl Attr {
    pub fn to_term(&self) -> attr_t {
        self.bits
    }
}

impl Style {
    fn normal(colors: ColorPair) -> Style {
        Style {
            colors: colors,
            attrs: NORMAL
        }
    }

    pub fn current() -> Style {
        let mut attrs: attr_t = 0;
        let mut colors: i16 = 0;
        attr_get(&mut attrs, &mut colors);
        Style {
            colors: ColorPair::from_term(colors),
            attrs: Attr::from_bits(attrs).unwrap_or(NORMAL)
        }
    }

    // pub fn context(&self) -> StyleContext {
    //     self.set();
    //     StyleContext { prev_style: Style::current() }
    // }

}

// impl Drop for StyleContext {
//     fn drop(&mut self) {
//         self.prev_style.set();
//     }
// }
<|endoftext|>


<commit_before>//! Syntax Tree library used throughout the rust analyzer.
//!
//! Properties:
//!   - easy and fast incremental re-parsing
//!   - graceful handling of errors
//!   - full-fidelity representation (*any* text can be precisely represented as
//!     a syntax tree)
//!
//! For more information, see the [RFC]. Current implementation is inspired by
//! the [Swift] one.
//!
//! The most interesting modules here are `syntax_node` (which defines concrete
//! syntax tree) and `ast` (which defines abstract syntax tree on top of the
//! CST). The actual parser live in a separate `parser` crate, though the
//! lexer lives in this crate.
//!
//! See `api_walkthrough` test in this file for a quick API tour!
//!
//! [RFC]: <https://github.com/rust-lang/rfcs/pull/2256>
//! [Swift]: <https://github.com/apple/swift/blob/13d593df6f359d0cb2fc81cfaac273297c539455/lib/Syntax/README.md>

#[allow(unused)]
macro_rules! eprintln {
    ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
}

mod syntax_node;
mod syntax_error;
mod parsing;
mod validation;
mod ptr;
mod token_text;
#[cfg(test)]
mod tests;

pub mod display;
pub mod algo;
pub mod ast;
#[doc(hidden)]
pub mod fuzz;
pub mod utils;
pub mod ted;

use std::{marker::PhantomData, sync::Arc};

use stdx::format_to;
use text_edit::Indel;

pub use crate::{
    ast::{AstNode, AstToken},
    ptr::{AstPtr, SyntaxNodePtr},
    syntax_error::SyntaxError,
    syntax_node::{
        PreorderWithTokens, SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren,
        SyntaxToken, SyntaxTreeBuilder,
    },
    token_text::TokenText,
};
pub use parser::{SyntaxKind, T};
pub use rowan::{
    Direction, GreenNode, NodeOrToken, SyntaxText, TextRange, TextSize, TokenAtOffset, WalkEvent,
};
pub use smol_str::SmolStr;

/// `Parse` is the result of the parsing: a syntax tree and a collection of
/// errors.
///
/// Note that we always produce a syntax tree, even for completely invalid
/// files.
#[derive(Debug, PartialEq, Eq)]
pub struct Parse<T> {
    green: GreenNode,
    errors: Arc<Vec<SyntaxError>>,
    _ty: PhantomData<fn() -> T>,
}

impl<T> Clone for Parse<T> {
    fn clone(&self) -> Parse<T> {
        Parse { green: self.green.clone(), errors: self.errors.clone(), _ty: PhantomData }
    }
}

impl<T> Parse<T> {
    fn new(green: GreenNode, errors: Vec<SyntaxError>) -> Parse<T> {
        Parse { green, errors: Arc::new(errors), _ty: PhantomData }
    }

    pub fn syntax_node(&self) -> SyntaxNode {
        SyntaxNode::new_root(self.green.clone())
    }
    pub fn errors(&self) -> &[SyntaxError] {
        &*self.errors
    }
}

impl<T: AstNode> Parse<T> {
    pub fn to_syntax(self) -> Parse<SyntaxNode> {
        Parse { green: self.green, errors: self.errors, _ty: PhantomData }
    }

    pub fn tree(&self) -> T {
        T::cast(self.syntax_node()).unwrap()
    }

    pub fn ok(self) -> Result<T, Arc<Vec<SyntaxError>>> {
        if self.errors.is_empty() {
            Ok(self.tree())
        } else {
            Err(self.errors)
        }
    }
}

impl Parse<SyntaxNode> {
    pub fn cast<N: AstNode>(self) -> Option<Parse<N>> {
        if N::cast(self.syntax_node()).is_some() {
            Some(Parse { green: self.green, errors: self.errors, _ty: PhantomData })
        } else {
            None
        }
    }
}

impl Parse<SourceFile> {
    pub fn debug_dump(&self) -> String {
        let mut buf = format!("{:#?}", self.tree().syntax());
        for err in self.errors.iter() {
            format_to!(buf, "error {:?}: {}\n", err.range(), err);
        }
        buf
    }

    pub fn reparse(&self, indel: &Indel) -> Parse<SourceFile> {
        self.incremental_reparse(indel).unwrap_or_else(|| self.full_reparse(indel))
    }

    fn incremental_reparse(&self, indel: &Indel) -> Option<Parse<SourceFile>> {
        // FIXME: validation errors are not handled here
        parsing::incremental_reparse(self.tree().syntax(), indel, self.errors.to_vec()).map(
            |(green_node, errors, _reparsed_range)| Parse {
                green: green_node,
                errors: Arc::new(errors),
                _ty: PhantomData,
            },
        )
    }

    fn full_reparse(&self, indel: &Indel) -> Parse<SourceFile> {
        let mut text = self.tree().syntax().text().to_string();
        indel.apply(&mut text);
        SourceFile::parse(&text)
    }
}

/// `SourceFile` represents a parse tree for a single Rust file.
pub use crate::ast::SourceFile;

impl SourceFile {
    pub fn parse(text: &str) -> Parse<SourceFile> {
        let (green, mut errors) = parsing::parse_text(text);
        let root = SyntaxNode::new_root(green.clone());

        errors.extend(validation::validate(&root));

        assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE);
        Parse { green, errors: Arc::new(errors), _ty: PhantomData }
    }
}

// FIXME: `parse` functions shouldn't hang directly from AST nodes, and they
// shouldn't return `Result`.
//
// We need a dedicated module for parser entry points, and they should always
// return `Parse`.

impl ast::Path {
    /// Returns `text`, parsed as a path, but only if it has no errors.
    pub fn parse(text: &str) -> Result<Self, ()> {
        parsing::parse_text_as(text, parser::ParserEntryPoint::Path)
    }
}

impl ast::Expr {
    /// Returns `text`, parsed as an expression, but only if it has no errors.
    pub fn parse(text: &str) -> Result<Self, ()> {
        parsing::parse_text_as(text, parser::ParserEntryPoint::Expr)
    }
}

impl ast::Attr {
    /// Returns `text`, parsed as an attribute, but only if it has no errors.
    pub fn parse(text: &str) -> Result<Self, ()> {
        parsing::parse_text_as(text, parser::ParserEntryPoint::Attr)
    }
}

/// Matches a `SyntaxNode` against an `ast` type.
///
/// # Example:
///
/// ```ignore
/// match_ast! {
///     match node {
///         ast::CallExpr(it) => { ... },
///         ast::MethodCallExpr(it) => { ... },
///         ast::MacroCall(it) => { ... },
///         _ => None,
///     }
/// }
/// ```
#[macro_export]
macro_rules! match_ast {
    (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };

    (match ($node:expr) {
        $( ast::$ast:ident($it:pat) => $res:expr, )*
        _ => $catch_all:expr $(,)?
    }) => {{
        $( if let Some($it) = ast::$ast::cast($node.clone()) { $res } else )*
        { $catch_all }
    }};
}

/// This test does not assert anything and instead just shows off the crate's
/// API.
#[test]
fn api_walkthrough() {
    use ast::{HasModuleItem, HasName};

    let source_code = "
        fn foo() {
            1 + 1
        }
    ";
    // `SourceFile` is the main entry point.
    //
    // The `parse` method returns a `Parse` -- a pair of syntax tree and a list
    // of errors. That is, syntax tree is constructed even in presence of errors.
    let parse = SourceFile::parse(source_code);
    assert!(parse.errors().is_empty());

    // The `tree` method returns an owned syntax node of type `SourceFile`.
    // Owned nodes are cheap: inside, they are `Rc` handles to the underling data.
    let file: SourceFile = parse.tree();

    // `SourceFile` is the root of the syntax tree. We can iterate file's items.
    // Let's fetch the `foo` function.
    let mut func = None;
    for item in file.items() {
        match item {
            ast::Item::Fn(f) => func = Some(f),
            _ => unreachable!(),
        }
    }
    let func: ast::Fn = func.unwrap();

    // Each AST node has a bunch of getters for children. All getters return
    // `Option`s though, to account for incomplete code. Some getters are common
    // for several kinds of node. In this case, a trait like `ast::NameOwner`
    // usually exists. By convention, all ast types should be used with `ast::`
    // qualifier.
    let name: Option<ast::Name> = func.name();
    let name = name.unwrap();
    assert_eq!(name.text(), "foo");

    // Let's get the `1 + 1` expression!
    let body: ast::BlockExpr = func.body().unwrap();
    let stmt_list: ast::StmtList = body.stmt_list().unwrap();
    let expr: ast::Expr = stmt_list.tail_expr().unwrap();

    // Enums are used to group related ast nodes together, and can be used for
    // matching. However, because there are no public fields, it's possible to
    // match only the top level enum: that is the price we pay for increased API
    // flexibility
    let bin_expr: &ast::BinExpr = match &expr {
        ast::Expr::BinExpr(e) => e,
        _ => unreachable!(),
    };

    // Besides the "typed" AST API, there's an untyped CST one as well.
    // To switch from AST to CST, call `.syntax()` method:
    let expr_syntax: &SyntaxNode = expr.syntax();

    // Note how `expr` and `bin_expr` are in fact the same node underneath:
    assert!(expr_syntax == bin_expr.syntax());

    // To go from CST to AST, `AstNode::cast` function is used:
    let _expr: ast::Expr = match ast::Expr::cast(expr_syntax.clone()) {
        Some(e) => e,
        None => unreachable!(),
    };

    // The two properties each syntax node has is a `SyntaxKind`:
    assert_eq!(expr_syntax.kind(), SyntaxKind::BIN_EXPR);

    // And text range:
    assert_eq!(expr_syntax.text_range(), TextRange::new(32.into(), 37.into()));

    // You can get node's text as a `SyntaxText` object, which will traverse the
    // tree collecting token's text:
    let text: SyntaxText = expr_syntax.text();
    assert_eq!(text.to_string(), "1 + 1");

    // There's a bunch of traversal methods on `SyntaxNode`:
    assert_eq!(expr_syntax.parent().as_ref(), Some(stmt_list.syntax()));
    assert_eq!(stmt_list.syntax().first_child_or_token().map(|it| it.kind()), Some(T!['{']));
    assert_eq!(
        expr_syntax.next_sibling_or_token().map(|it| it.kind()),
        Some(SyntaxKind::WHITESPACE)
    );

    // As well as some iterator helpers:
    let f = expr_syntax.ancestors().find_map(ast::Fn::cast);
    assert_eq!(f, Some(func));
    assert!(expr_syntax.siblings_with_tokens(Direction::Next).any(|it| it.kind() == T!['}']));
    assert_eq!(
        expr_syntax.descendants_with_tokens().count(),
        8, // 5 tokens `1`, ` `, `+`, ` `, `!`
           // 2 child literal expressions: `1`, `1`
           // 1 the node itself: `1 + 1`
    );

    // There's also a `preorder` method with a more fine-grained iteration control:
    let mut buf = String::new();
    let mut indent = 0;
    for event in expr_syntax.preorder_with_tokens() {
        match event {
            WalkEvent::Enter(node) => {
                let text = match &node {
                    NodeOrToken::Node(it) => it.text().to_string(),
                    NodeOrToken::Token(it) => it.text().to_string(),
                };
                format_to!(buf, "{:indent$}{:?} {:?}\n", " ", text, node.kind(), indent = indent);
                indent += 2;
            }
            WalkEvent::Leave(_) => indent -= 2,
        }
    }
    assert_eq!(indent, 0);
    assert_eq!(
        buf.trim(),
        r#"
"1 + 1" BIN_EXPR
  "1" LITERAL
    "1" INT_NUMBER
  " " WHITESPACE
  "+" PLUS
  " " WHITESPACE
  "1" LITERAL
    "1" INT_NUMBER
"#
        .trim()
    );

    // To recursively process the tree, there are three approaches:
    // 1. explicitly call getter methods on AST nodes.
    // 2. use descendants and `AstNode::cast`.
    // 3. use descendants and `match_ast!`.
    //
    // Here's how the first one looks like:
    let exprs_cast: Vec<String> = file
        .syntax()
        .descendants()
        .filter_map(ast::Expr::cast)
        .map(|expr| expr.syntax().text().to_string())
        .collect();

    // An alternative is to use a macro.
    let mut exprs_visit = Vec::new();
    for node in file.syntax().descendants() {
        match_ast! {
            match node {
                ast::Expr(it) => {
                    let res = it.syntax().text().to_string();
                    exprs_visit.push(res);
                },
                _ => (),
            }
        }
    }
    assert_eq!(exprs_cast, exprs_visit);
}
<commit_msg>dead code<commit_after>//! Syntax Tree library used throughout the rust analyzer.
//!
//! Properties:
//!   - easy and fast incremental re-parsing
//!   - graceful handling of errors
//!   - full-fidelity representation (*any* text can be precisely represented as
//!     a syntax tree)
//!
//! For more information, see the [RFC]. Current implementation is inspired by
//! the [Swift] one.
//!
//! The most interesting modules here are `syntax_node` (which defines concrete
//! syntax tree) and `ast` (which defines abstract syntax tree on top of the
//! CST). The actual parser live in a separate `parser` crate, though the
//! lexer lives in this crate.
//!
//! See `api_walkthrough` test in this file for a quick API tour!
//!
//! [RFC]: <https://github.com/rust-lang/rfcs/pull/2256>
//! [Swift]: <https://github.com/apple/swift/blob/13d593df6f359d0cb2fc81cfaac273297c539455/lib/Syntax/README.md>

#[allow(unused)]
macro_rules! eprintln {
    ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
}

mod syntax_node;
mod syntax_error;
mod parsing;
mod validation;
mod ptr;
mod token_text;
#[cfg(test)]
mod tests;

pub mod display;
pub mod algo;
pub mod ast;
#[doc(hidden)]
pub mod fuzz;
pub mod utils;
pub mod ted;

use std::{marker::PhantomData, sync::Arc};

use stdx::format_to;
use text_edit::Indel;

pub use crate::{
    ast::{AstNode, AstToken},
    ptr::{AstPtr, SyntaxNodePtr},
    syntax_error::SyntaxError,
    syntax_node::{
        PreorderWithTokens, SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren,
        SyntaxToken, SyntaxTreeBuilder,
    },
    token_text::TokenText,
};
pub use parser::{SyntaxKind, T};
pub use rowan::{
    Direction, GreenNode, NodeOrToken, SyntaxText, TextRange, TextSize, TokenAtOffset, WalkEvent,
};
pub use smol_str::SmolStr;

/// `Parse` is the result of the parsing: a syntax tree and a collection of
/// errors.
///
/// Note that we always produce a syntax tree, even for completely invalid
/// files.
#[derive(Debug, PartialEq, Eq)]
pub struct Parse<T> {
    green: GreenNode,
    errors: Arc<Vec<SyntaxError>>,
    _ty: PhantomData<fn() -> T>,
}

impl<T> Clone for Parse<T> {
    fn clone(&self) -> Parse<T> {
        Parse { green: self.green.clone(), errors: self.errors.clone(), _ty: PhantomData }
    }
}

impl<T> Parse<T> {
    fn new(green: GreenNode, errors: Vec<SyntaxError>) -> Parse<T> {
        Parse { green, errors: Arc::new(errors), _ty: PhantomData }
    }

    pub fn syntax_node(&self) -> SyntaxNode {
        SyntaxNode::new_root(self.green.clone())
    }
    pub fn errors(&self) -> &[SyntaxError] {
        &*self.errors
    }
}

impl<T: AstNode> Parse<T> {
    pub fn to_syntax(self) -> Parse<SyntaxNode> {
        Parse { green: self.green, errors: self.errors, _ty: PhantomData }
    }

    pub fn tree(&self) -> T {
        T::cast(self.syntax_node()).unwrap()
    }

    pub fn ok(self) -> Result<T, Arc<Vec<SyntaxError>>> {
        if self.errors.is_empty() {
            Ok(self.tree())
        } else {
            Err(self.errors)
        }
    }
}

impl Parse<SyntaxNode> {
    pub fn cast<N: AstNode>(self) -> Option<Parse<N>> {
        if N::cast(self.syntax_node()).is_some() {
            Some(Parse { green: self.green, errors: self.errors, _ty: PhantomData })
        } else {
            None
        }
    }
}

impl Parse<SourceFile> {
    pub fn debug_dump(&self) -> String {
        let mut buf = format!("{:#?}", self.tree().syntax());
        for err in self.errors.iter() {
            format_to!(buf, "error {:?}: {}\n", err.range(), err);
        }
        buf
    }

    pub fn reparse(&self, indel: &Indel) -> Parse<SourceFile> {
        self.incremental_reparse(indel).unwrap_or_else(|| self.full_reparse(indel))
    }

    fn incremental_reparse(&self, indel: &Indel) -> Option<Parse<SourceFile>> {
        // FIXME: validation errors are not handled here
        parsing::incremental_reparse(self.tree().syntax(), indel, self.errors.to_vec()).map(
            |(green_node, errors, _reparsed_range)| Parse {
                green: green_node,
                errors: Arc::new(errors),
                _ty: PhantomData,
            },
        )
    }

    fn full_reparse(&self, indel: &Indel) -> Parse<SourceFile> {
        let mut text = self.tree().syntax().text().to_string();
        indel.apply(&mut text);
        SourceFile::parse(&text)
    }
}

/// `SourceFile` represents a parse tree for a single Rust file.
pub use crate::ast::SourceFile;

impl SourceFile {
    pub fn parse(text: &str) -> Parse<SourceFile> {
        let (green, mut errors) = parsing::parse_text(text);
        let root = SyntaxNode::new_root(green.clone());

        errors.extend(validation::validate(&root));

        assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE);
        Parse { green, errors: Arc::new(errors), _ty: PhantomData }
    }
}

// FIXME: `parse` functions shouldn't hang directly from AST nodes, and they
// shouldn't return `Result`.
//
// We need a dedicated module for parser entry points, and they should always
// return `Parse`.

impl ast::Path {
    /// Returns `text`, parsed as a path, but only if it has no errors.
    pub fn parse(text: &str) -> Result<Self, ()> {
        parsing::parse_text_as(text, parser::ParserEntryPoint::Path)
    }
}

impl ast::Expr {
    /// Returns `text`, parsed as an expression, but only if it has no errors.
    pub fn parse(text: &str) -> Result<Self, ()> {
        parsing::parse_text_as(text, parser::ParserEntryPoint::Expr)
    }
}

/// Matches a `SyntaxNode` against an `ast` type.
///
/// # Example:
///
/// ```ignore
/// match_ast! {
///     match node {
///         ast::CallExpr(it) => { ... },
///         ast::MethodCallExpr(it) => { ... },
///         ast::MacroCall(it) => { ... },
///         _ => None,
///     }
/// }
/// ```
#[macro_export]
macro_rules! match_ast {
    (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };

    (match ($node:expr) {
        $( ast::$ast:ident($it:pat) => $res:expr, )*
        _ => $catch_all:expr $(,)?
    }) => {{
        $( if let Some($it) = ast::$ast::cast($node.clone()) { $res } else )*
        { $catch_all }
    }};
}

/// This test does not assert anything and instead just shows off the crate's
/// API.
#[test]
fn api_walkthrough() {
    use ast::{HasModuleItem, HasName};

    let source_code = "
        fn foo() {
            1 + 1
        }
    ";
    // `SourceFile` is the main entry point.
    //
    // The `parse` method returns a `Parse` -- a pair of syntax tree and a list
    // of errors. That is, syntax tree is constructed even in presence of errors.
    let parse = SourceFile::parse(source_code);
    assert!(parse.errors().is_empty());

    // The `tree` method returns an owned syntax node of type `SourceFile`.
    // Owned nodes are cheap: inside, they are `Rc` handles to the underling data.
    let file: SourceFile = parse.tree();

    // `SourceFile` is the root of the syntax tree. We can iterate file's items.
    // Let's fetch the `foo` function.
    let mut func = None;
    for item in file.items() {
        match item {
            ast::Item::Fn(f) => func = Some(f),
            _ => unreachable!(),
        }
    }
    let func: ast::Fn = func.unwrap();

    // Each AST node has a bunch of getters for children. All getters return
    // `Option`s though, to account for incomplete code. Some getters are common
    // for several kinds of node. In this case, a trait like `ast::NameOwner`
    // usually exists. By convention, all ast types should be used with `ast::`
    // qualifier.
    let name: Option<ast::Name> = func.name();
    let name = name.unwrap();
    assert_eq!(name.text(), "foo");

    // Let's get the `1 + 1` expression!
    let body: ast::BlockExpr = func.body().unwrap();
    let stmt_list: ast::StmtList = body.stmt_list().unwrap();
    let expr: ast::Expr = stmt_list.tail_expr().unwrap();

    // Enums are used to group related ast nodes together, and can be used for
    // matching. However, because there are no public fields, it's possible to
    // match only the top level enum: that is the price we pay for increased API
    // flexibility
    let bin_expr: &ast::BinExpr = match &expr {
        ast::Expr::BinExpr(e) => e,
        _ => unreachable!(),
    };

    // Besides the "typed" AST API, there's an untyped CST one as well.
    // To switch from AST to CST, call `.syntax()` method:
    let expr_syntax: &SyntaxNode = expr.syntax();

    // Note how `expr` and `bin_expr` are in fact the same node underneath:
    assert!(expr_syntax == bin_expr.syntax());

    // To go from CST to AST, `AstNode::cast` function is used:
    let _expr: ast::Expr = match ast::Expr::cast(expr_syntax.clone()) {
        Some(e) => e,
        None => unreachable!(),
    };

    // The two properties each syntax node has is a `SyntaxKind`:
    assert_eq!(expr_syntax.kind(), SyntaxKind::BIN_EXPR);

    // And text range:
    assert_eq!(expr_syntax.text_range(), TextRange::new(32.into(), 37.into()));

    // You can get node's text as a `SyntaxText` object, which will traverse the
    // tree collecting token's text:
    let text: SyntaxText = expr_syntax.text();
    assert_eq!(text.to_string(), "1 + 1");

    // There's a bunch of traversal methods on `SyntaxNode`:
    assert_eq!(expr_syntax.parent().as_ref(), Some(stmt_list.syntax()));
    assert_eq!(stmt_list.syntax().first_child_or_token().map(|it| it.kind()), Some(T!['{']));
    assert_eq!(
        expr_syntax.next_sibling_or_token().map(|it| it.kind()),
        Some(SyntaxKind::WHITESPACE)
    );

    // As well as some iterator helpers:
    let f = expr_syntax.ancestors().find_map(ast::Fn::cast);
    assert_eq!(f, Some(func));
    assert!(expr_syntax.siblings_with_tokens(Direction::Next).any(|it| it.kind() == T!['}']));
    assert_eq!(
        expr_syntax.descendants_with_tokens().count(),
        8, // 5 tokens `1`, ` `, `+`, ` `, `!`
           // 2 child literal expressions: `1`, `1`
           // 1 the node itself: `1 + 1`
    );

    // There's also a `preorder` method with a more fine-grained iteration control:
    let mut buf = String::new();
    let mut indent = 0;
    for event in expr_syntax.preorder_with_tokens() {
        match event {
            WalkEvent::Enter(node) => {
                let text = match &node {
                    NodeOrToken::Node(it) => it.text().to_string(),
                    NodeOrToken::Token(it) => it.text().to_string(),
                };
                format_to!(buf, "{:indent$}{:?} {:?}\n", " ", text, node.kind(), indent = indent);
                indent += 2;
            }
            WalkEvent::Leave(_) => indent -= 2,
        }
    }
    assert_eq!(indent, 0);
    assert_eq!(
        buf.trim(),
        r#"
"1 + 1" BIN_EXPR
  "1" LITERAL
    "1" INT_NUMBER
  " " WHITESPACE
  "+" PLUS
  " " WHITESPACE
  "1" LITERAL
    "1" INT_NUMBER
"#
        .trim()
    );

    // To recursively process the tree, there are three approaches:
    // 1. explicitly call getter methods on AST nodes.
    // 2. use descendants and `AstNode::cast`.
    // 3. use descendants and `match_ast!`.
    //
    // Here's how the first one looks like:
    let exprs_cast: Vec<String> = file
        .syntax()
        .descendants()
        .filter_map(ast::Expr::cast)
        .map(|expr| expr.syntax().text().to_string())
        .collect();

    // An alternative is to use a macro.
    let mut exprs_visit = Vec::new();
    for node in file.syntax().descendants() {
        match_ast! {
            match node {
                ast::Expr(it) => {
                    let res = it.syntax().text().to_string();
                    exprs_visit.push(res);
                },
                _ => (),
            }
        }
    }
    assert_eq!(exprs_cast, exprs_visit);
}
<|endoftext|>


<commit_before>//! The `conf` module contains functions for loading and saving game
//! configurations.
//!
//! A [`Conf`](struct.Conf.html) struct is used to create a config file
//! which specifies hardware setup stuff, mostly video display settings.
//!
//! By default a ggez game will search its resource paths for a `/conf.toml`
//! file and load values from it when the [`Context`](../struct.Context.html) is created.  This file
//! must be complete (ie you cannot just fill in some fields and have the
//! rest be default) and provides a nice way to specify settings that
//! can be tweaked such as window resolution, multisampling options, etc.
//! If no file is found, it will create a `Conf` object from the settings
//! passed to the [`ContextBuilder`](../struct.ContextBuilder.html).

use std::convert::TryFrom;
use std::io;

use crate::error::{GameError, GameResult};

/// Possible fullscreen modes.
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum FullscreenType {
    /// Windowed mode.
    Windowed,
    /// True fullscreen, which used to be preferred 'cause it can have
    /// small performance benefits over windowed fullscreen.
    ///
    /// Also it allows us to set different resolutions.
    True,
    /// Windowed fullscreen, generally preferred over real fullscreen
    /// these days 'cause it plays nicer with multiple monitors.
    Desktop,
}

/// A builder structure containing window settings
/// that can be set at runtime and changed with [`graphics::set_mode()`](../graphics/fn.set_mode.html).
///
/// Defaults:
///
/// ```rust
/// # use ggez::conf::*;
/// # fn main() { assert_eq!(
/// WindowMode {
///     width: 800.0,
///     height: 600.0,
///     maximized: false,
///     fullscreen_type: FullscreenType::Windowed,
///     borderless: false,
///     min_width: 0.0,
///     max_width: 0.0,
///     min_height: 0.0,
///     max_height: 0.0,
///     resizable: false,
///     visible: true,
///     resize_on_scale_factor_change: false,
/// }
/// # , WindowMode::default());}
/// ```
#[derive(Debug, Copy, Clone, SmartDefault, Serialize, Deserialize, PartialEq)]
pub struct WindowMode {
    /// Window width in physical pixels
    #[default = 800.0]
    pub width: f32,
    /// Window height in physical pixels
    #[default = 600.0]
    pub height: f32,
    /// Whether or not to maximize the window
    #[default = false]
    pub maximized: bool,
    /// Fullscreen type
    #[default(FullscreenType::Windowed)]
    pub fullscreen_type: FullscreenType,
    /// Whether or not to show window decorations
    #[default = false]
    pub borderless: bool,
    /// Minimum width for resizable windows; 0 means no limit
    #[default = 0.0]
    pub min_width: f32,
    /// Minimum height for resizable windows; 0 means no limit
    #[default = 0.0]
    pub min_height: f32,
    /// Maximum width for resizable windows; 0 means no limit
    #[default = 0.0]
    pub max_width: f32,
    /// Maximum height for resizable windows; 0 means no limit
    #[default = 0.0]
    pub max_height: f32,
    /// Whether or not the window is resizable
    #[default = false]
    pub resizable: bool,
    /// Whether this window should displayed (true) or hidden (false)
    #[default = true]
    pub visible: bool,
    /// Whether this window should change its size in physical pixels
    /// when its hidpi factor changes, i.e. when [`WindowEvent::ScaleFactorChanged`](https://docs.rs/winit/0.25.0/winit/event/enum.WindowEvent.html#variant.ScaleFactorChanged)
    /// is fired.
    ///
    /// You usually want this to be false, since the window suddenly changing size may break your game.
    /// Setting this to true may be desirable if you plan for it and want your window to behave like
    /// windows of other programs when being dragged from one screen to another, for example.
    ///
    /// For more context on this take a look at [this conversation](https://github.com/ggez/ggez/pull/949#issuecomment-854731226).
    #[default = false]
    pub resize_on_scale_factor_change: bool,
}

impl WindowMode {
    /// Set default window size, or screen resolution in true fullscreen mode.
    #[must_use]
    pub fn dimensions(mut self, width: f32, height: f32) -> Self {
        self.width = width;
        self.height = height;
        self
    }

    /// Set whether the window should be maximized.
    #[must_use]
    pub fn maximized(mut self, maximized: bool) -> Self {
        self.maximized = maximized;
        self
    }

    /// Set the fullscreen type.
    #[must_use]
    pub fn fullscreen_type(mut self, fullscreen_type: FullscreenType) -> Self {
        self.fullscreen_type = fullscreen_type;
        self
    }

    /// Set whether a window should be borderless in windowed mode.
    #[must_use]
    pub fn borderless(mut self, borderless: bool) -> Self {
        self.borderless = borderless;
        self
    }

    /// Set minimum window dimensions for windowed mode.
    #[must_use]
    pub fn min_dimensions(mut self, width: f32, height: f32) -> Self {
        self.min_width = width;
        self.min_height = height;
        self
    }

    /// Set maximum window dimensions for windowed mode.
    #[must_use]
    pub fn max_dimensions(mut self, width: f32, height: f32) -> Self {
        self.max_width = width;
        self.max_height = height;
        self
    }

    /// Set resizable.
    #[must_use]
    pub fn resizable(mut self, resizable: bool) -> Self {
        self.resizable = resizable;
        self
    }

    /// Set visibility
    #[must_use]
    pub fn visible(mut self, visible: bool) -> Self {
        self.visible = visible;
        self
    }

    /// Set whether to resize when the hidpi factor changes
    #[must_use]
    pub fn resize_on_scale_factor_change(mut self, resize_on_scale_factor_change: bool) -> Self {
        self.resize_on_scale_factor_change = resize_on_scale_factor_change;
        self
    }
}

/// A builder structure containing window settings
/// that must be set at init time and cannot be changed afterwards.
///
/// Defaults:
///
/// ```rust
/// # use ggez::conf::*;
/// # fn main() { assert_eq!(
/// WindowSetup {
///     title: "An easy, good game".to_owned(),
///     samples: NumSamples::One,
///     vsync: true,
///     icon: "".to_owned(),
///     srgb: true,
/// }
/// # , WindowSetup::default()); }
/// ```
#[derive(Debug, Clone, SmartDefault, Serialize, Deserialize, PartialEq)]
pub struct WindowSetup {
    /// The window title.
    #[default(String::from("An easy, good game"))]
    pub title: String,
    /// Number of samples to use for multisample anti-aliasing.
    #[default(NumSamples::One)]
    pub samples: NumSamples,
    /// Whether or not to enable vsync.
    #[default = true]
    pub vsync: bool,
    /// A file path to the window's icon.
    /// It takes a path rooted in the `resources` directory (see the [`filesystem`](../filesystem/index.html)
    /// module for details), and an empty string results in a blank/default icon.
    #[default(String::new())]
    pub icon: String,
    /// Whether or not to enable sRGB (gamma corrected color)
    /// handling on the display.
    #[default = true]
    pub srgb: bool,
}

impl WindowSetup {
    /// Set window title.
    #[must_use]
    pub fn title(mut self, title: &str) -> Self {
        self.title = title.to_owned();
        self
    }

    /// Set number of samples to use for multisample anti-aliasing.
    #[must_use]
    pub fn samples(mut self, samples: NumSamples) -> Self {
        self.samples = samples;
        self
    }

    /// Set whether vsync is enabled.
    #[must_use]
    pub fn vsync(mut self, vsync: bool) -> Self {
        self.vsync = vsync;
        self
    }

    /// Set the window's icon.
    #[must_use]
    pub fn icon(mut self, icon: &str) -> Self {
        self.icon = icon.to_owned();
        self
    }

    /// Set sRGB color mode.
    #[must_use]
    pub fn srgb(mut self, active: bool) -> Self {
        self.srgb = active;
        self
    }
}

/// Possible graphics backends.
/// The default is `Primary`.
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, SmartDefault)]
#[serde(tag = "type")]
pub enum Backend {
    /// Primary comprises of Vulkan + Metal + DX12 (each as a fallback for the other).
    ///
    /// These APIs have first-class support from WGPU and from the platforms that support them.
    #[default]
    Primary,
    /// Secondary comprises of OpenGL + DX11 (each as a fallback for the other).
    ///
    /// These APIs may have issues and may be deprecated by some platforms. This is not recommended.
    Secondary,
    /// Use the Khronos Vulkan API.
    Vulkan,
    /// Use the Apple Metal API.
    Metal,
    /// Use the Microsoft DirectX 12 API.
    Dx12,
    /// Use the Microsoft DirectX 11 API. This is not a recommended backend.
    Dx11,
    /// Use the Khronos OpenGL API. This is not a recommended backend.
    Gl,
    /// Use the WebGPU API. Targets the web.
    BrowserWebGpu,
}

/// The possible number of samples for multisample anti-aliasing.
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum NumSamples {
    /// One sample
    One = 1,
    /// Two samples
    Two = 2,
    /// Four samples
    Four = 4,
    /// Eight samples
    Eight = 8,
    /// Sixteen samples
    Sixteen = 16,
}

impl TryFrom<u8> for NumSamples {
    type Error = GameError;
    fn try_from(i: u8) -> Result<Self, Self::Error> {
        match i {
            1 => Ok(NumSamples::One),
            2 => Ok(NumSamples::Two),
            4 => Ok(NumSamples::Four),
            8 => Ok(NumSamples::Eight),
            16 => Ok(NumSamples::Sixteen),
            _ => Err(GameError::ConfigError(String::from(
                "Invalid number of samples",
            ))),
        }
    }
}

impl From<NumSamples> for u8 {
    fn from(ns: NumSamples) -> u8 {
        ns as u8
    }
}

/// A structure containing configuration data
/// for the game engine.
///
/// Defaults:
///
/// ```rust
/// # use ggez::conf::*;
/// # fn main() { assert_eq!(
/// Conf {
///     window_mode: WindowMode::default(),
///     window_setup: WindowSetup::default(),
///     backend: Backend::default(),
///     modules: ModuleConf::default(),
/// }
/// # , Conf::default()); }
/// ```
#[derive(Serialize, Deserialize, Debug, PartialEq, SmartDefault, Clone)]
pub struct Conf {
    /// Window setting information that can be set at runtime
    pub window_mode: WindowMode,
    /// Window setting information that must be set at init-time
    pub window_setup: WindowSetup,
    /// Graphics backend configuration
    pub backend: Backend,
}

impl Conf {
    /// Same as `Conf::default()`
    pub fn new() -> Self {
        Self::default()
    }

    /// Load a TOML file from the given `Read` and attempts to parse
    /// a `Conf` from it.
    pub fn from_toml_file<R: io::Read>(file: &mut R) -> GameResult<Conf> {
        let mut s = String::new();
        let _ = file.read_to_string(&mut s)?;
        let decoded = toml::from_str(&s)?;
        Ok(decoded)
    }

    /// Saves the `Conf` to the given `Write` object,
    /// formatted as TOML.
    pub fn to_toml_file<W: io::Write>(&self, file: &mut W) -> GameResult {
        let s = toml::to_vec(self)?;
        file.write_all(&s)?;
        Ok(())
    }

    /// Sets the window mode
    #[must_use]
    pub fn window_mode(mut self, window_mode: WindowMode) -> Self {
        self.window_mode = window_mode;
        self
    }

    /// Sets the backend
    #[must_use]
    pub fn backend(mut self, backend: Backend) -> Self {
        self.backend = backend;
        self
    }
}

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

    /// Tries to encode and decode a `Conf` object
    /// and makes sure it gets the same result it had.
    #[test]
    fn headless_encode_round_trip() {
        let c1 = conf::Conf::new();
        let mut writer = Vec::new();
        let _c = c1.to_toml_file(&mut writer).unwrap();
        let mut reader = writer.as_slice();
        let c2 = conf::Conf::from_toml_file(&mut reader).unwrap();
        assert_eq!(c1, c2);
    }
}
<commit_msg>hide unsupported sample counts<commit_after>//! The `conf` module contains functions for loading and saving game
//! configurations.
//!
//! A [`Conf`](struct.Conf.html) struct is used to create a config file
//! which specifies hardware setup stuff, mostly video display settings.
//!
//! By default a ggez game will search its resource paths for a `/conf.toml`
//! file and load values from it when the [`Context`](../struct.Context.html) is created.  This file
//! must be complete (ie you cannot just fill in some fields and have the
//! rest be default) and provides a nice way to specify settings that
//! can be tweaked such as window resolution, multisampling options, etc.
//! If no file is found, it will create a `Conf` object from the settings
//! passed to the [`ContextBuilder`](../struct.ContextBuilder.html).

use std::convert::TryFrom;
use std::io;

use crate::error::{GameError, GameResult};

/// Possible fullscreen modes.
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum FullscreenType {
    /// Windowed mode.
    Windowed,
    /// True fullscreen, which used to be preferred 'cause it can have
    /// small performance benefits over windowed fullscreen.
    ///
    /// Also it allows us to set different resolutions.
    True,
    /// Windowed fullscreen, generally preferred over real fullscreen
    /// these days 'cause it plays nicer with multiple monitors.
    Desktop,
}

/// A builder structure containing window settings
/// that can be set at runtime and changed with [`graphics::set_mode()`](../graphics/fn.set_mode.html).
///
/// Defaults:
///
/// ```rust
/// # use ggez::conf::*;
/// # fn main() { assert_eq!(
/// WindowMode {
///     width: 800.0,
///     height: 600.0,
///     maximized: false,
///     fullscreen_type: FullscreenType::Windowed,
///     borderless: false,
///     min_width: 0.0,
///     max_width: 0.0,
///     min_height: 0.0,
///     max_height: 0.0,
///     resizable: false,
///     visible: true,
///     resize_on_scale_factor_change: false,
/// }
/// # , WindowMode::default());}
/// ```
#[derive(Debug, Copy, Clone, SmartDefault, Serialize, Deserialize, PartialEq)]
pub struct WindowMode {
    /// Window width in physical pixels
    #[default = 800.0]
    pub width: f32,
    /// Window height in physical pixels
    #[default = 600.0]
    pub height: f32,
    /// Whether or not to maximize the window
    #[default = false]
    pub maximized: bool,
    /// Fullscreen type
    #[default(FullscreenType::Windowed)]
    pub fullscreen_type: FullscreenType,
    /// Whether or not to show window decorations
    #[default = false]
    pub borderless: bool,
    /// Minimum width for resizable windows; 0 means no limit
    #[default = 0.0]
    pub min_width: f32,
    /// Minimum height for resizable windows; 0 means no limit
    #[default = 0.0]
    pub min_height: f32,
    /// Maximum width for resizable windows; 0 means no limit
    #[default = 0.0]
    pub max_width: f32,
    /// Maximum height for resizable windows; 0 means no limit
    #[default = 0.0]
    pub max_height: f32,
    /// Whether or not the window is resizable
    #[default = false]
    pub resizable: bool,
    /// Whether this window should displayed (true) or hidden (false)
    #[default = true]
    pub visible: bool,
    /// Whether this window should change its size in physical pixels
    /// when its hidpi factor changes, i.e. when [`WindowEvent::ScaleFactorChanged`](https://docs.rs/winit/0.25.0/winit/event/enum.WindowEvent.html#variant.ScaleFactorChanged)
    /// is fired.
    ///
    /// You usually want this to be false, since the window suddenly changing size may break your game.
    /// Setting this to true may be desirable if you plan for it and want your window to behave like
    /// windows of other programs when being dragged from one screen to another, for example.
    ///
    /// For more context on this take a look at [this conversation](https://github.com/ggez/ggez/pull/949#issuecomment-854731226).
    #[default = false]
    pub resize_on_scale_factor_change: bool,
}

impl WindowMode {
    /// Set default window size, or screen resolution in true fullscreen mode.
    #[must_use]
    pub fn dimensions(mut self, width: f32, height: f32) -> Self {
        self.width = width;
        self.height = height;
        self
    }

    /// Set whether the window should be maximized.
    #[must_use]
    pub fn maximized(mut self, maximized: bool) -> Self {
        self.maximized = maximized;
        self
    }

    /// Set the fullscreen type.
    #[must_use]
    pub fn fullscreen_type(mut self, fullscreen_type: FullscreenType) -> Self {
        self.fullscreen_type = fullscreen_type;
        self
    }

    /// Set whether a window should be borderless in windowed mode.
    #[must_use]
    pub fn borderless(mut self, borderless: bool) -> Self {
        self.borderless = borderless;
        self
    }

    /// Set minimum window dimensions for windowed mode.
    #[must_use]
    pub fn min_dimensions(mut self, width: f32, height: f32) -> Self {
        self.min_width = width;
        self.min_height = height;
        self
    }

    /// Set maximum window dimensions for windowed mode.
    #[must_use]
    pub fn max_dimensions(mut self, width: f32, height: f32) -> Self {
        self.max_width = width;
        self.max_height = height;
        self
    }

    /// Set resizable.
    #[must_use]
    pub fn resizable(mut self, resizable: bool) -> Self {
        self.resizable = resizable;
        self
    }

    /// Set visibility
    #[must_use]
    pub fn visible(mut self, visible: bool) -> Self {
        self.visible = visible;
        self
    }

    /// Set whether to resize when the hidpi factor changes
    #[must_use]
    pub fn resize_on_scale_factor_change(mut self, resize_on_scale_factor_change: bool) -> Self {
        self.resize_on_scale_factor_change = resize_on_scale_factor_change;
        self
    }
}

/// A builder structure containing window settings
/// that must be set at init time and cannot be changed afterwards.
///
/// Defaults:
///
/// ```rust
/// # use ggez::conf::*;
/// # fn main() { assert_eq!(
/// WindowSetup {
///     title: "An easy, good game".to_owned(),
///     samples: NumSamples::One,
///     vsync: true,
///     icon: "".to_owned(),
///     srgb: true,
/// }
/// # , WindowSetup::default()); }
/// ```
#[derive(Debug, Clone, SmartDefault, Serialize, Deserialize, PartialEq)]
pub struct WindowSetup {
    /// The window title.
    #[default(String::from("An easy, good game"))]
    pub title: String,
    /// Number of samples to use for multisample anti-aliasing.
    #[default(NumSamples::One)]
    pub samples: NumSamples,
    /// Whether or not to enable vsync.
    #[default = true]
    pub vsync: bool,
    /// A file path to the window's icon.
    /// It takes a path rooted in the `resources` directory (see the [`filesystem`](../filesystem/index.html)
    /// module for details), and an empty string results in a blank/default icon.
    #[default(String::new())]
    pub icon: String,
    /// Whether or not to enable sRGB (gamma corrected color)
    /// handling on the display.
    #[default = true]
    pub srgb: bool,
}

impl WindowSetup {
    /// Set window title.
    #[must_use]
    pub fn title(mut self, title: &str) -> Self {
        self.title = title.to_owned();
        self
    }

    /// Set number of samples to use for multisample anti-aliasing.
    #[must_use]
    pub fn samples(mut self, samples: NumSamples) -> Self {
        self.samples = samples;
        self
    }

    /// Set whether vsync is enabled.
    #[must_use]
    pub fn vsync(mut self, vsync: bool) -> Self {
        self.vsync = vsync;
        self
    }

    /// Set the window's icon.
    #[must_use]
    pub fn icon(mut self, icon: &str) -> Self {
        self.icon = icon.to_owned();
        self
    }

    /// Set sRGB color mode.
    #[must_use]
    pub fn srgb(mut self, active: bool) -> Self {
        self.srgb = active;
        self
    }
}

/// Possible graphics backends.
/// The default is `Primary`.
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, SmartDefault)]
#[serde(tag = "type")]
pub enum Backend {
    /// Primary comprises of Vulkan + Metal + DX12 (each as a fallback for the other).
    ///
    /// These APIs have first-class support from WGPU and from the platforms that support them.
    #[default]
    Primary,
    /// Secondary comprises of OpenGL + DX11 (each as a fallback for the other).
    ///
    /// These APIs may have issues and may be deprecated by some platforms. This is not recommended.
    Secondary,
    /// Use the Khronos Vulkan API.
    Vulkan,
    /// Use the Apple Metal API.
    Metal,
    /// Use the Microsoft DirectX 12 API.
    Dx12,
    /// Use the Microsoft DirectX 11 API. This is not a recommended backend.
    Dx11,
    /// Use the Khronos OpenGL API. This is not a recommended backend.
    Gl,
    /// Use the WebGPU API. Targets the web.
    BrowserWebGpu,
}

/// The possible number of samples for multisample anti-aliasing.
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum NumSamples {
    /// One sample
    One = 1,
    /* uncomment when WGPU supports more sample counts
    /// Two samples
    Two = 2,
    */
    /// Four samples
    Four = 4,
    /* uncomment when WGPU supports more sample counts
    /// Eight samples
    Eight = 8,
    /// Sixteen samples
    Sixteen = 16,
    */
}

impl TryFrom<u8> for NumSamples {
    type Error = GameError;
    fn try_from(i: u8) -> Result<Self, Self::Error> {
        match i {
            1 => Ok(NumSamples::One),
            //2 => Ok(NumSamples::Two),
            4 => Ok(NumSamples::Four),
            //8 => Ok(NumSamples::Eight),
            //16 => Ok(NumSamples::Sixteen),
            _ => Err(GameError::ConfigError(String::from(
                "Invalid number of samples",
            ))),
        }
    }
}

impl From<NumSamples> for u8 {
    fn from(ns: NumSamples) -> u8 {
        ns as u8
    }
}

/// A structure containing configuration data
/// for the game engine.
///
/// Defaults:
///
/// ```rust
/// # use ggez::conf::*;
/// # fn main() { assert_eq!(
/// Conf {
///     window_mode: WindowMode::default(),
///     window_setup: WindowSetup::default(),
///     backend: Backend::default(),
///     modules: ModuleConf::default(),
/// }
/// # , Conf::default()); }
/// ```
#[derive(Serialize, Deserialize, Debug, PartialEq, SmartDefault, Clone)]
pub struct Conf {
    /// Window setting information that can be set at runtime
    pub window_mode: WindowMode,
    /// Window setting information that must be set at init-time
    pub window_setup: WindowSetup,
    /// Graphics backend configuration
    pub backend: Backend,
}

impl Conf {
    /// Same as `Conf::default()`
    pub fn new() -> Self {
        Self::default()
    }

    /// Load a TOML file from the given `Read` and attempts to parse
    /// a `Conf` from it.
    pub fn from_toml_file<R: io::Read>(file: &mut R) -> GameResult<Conf> {
        let mut s = String::new();
        let _ = file.read_to_string(&mut s)?;
        let decoded = toml::from_str(&s)?;
        Ok(decoded)
    }

    /// Saves the `Conf` to the given `Write` object,
    /// formatted as TOML.
    pub fn to_toml_file<W: io::Write>(&self, file: &mut W) -> GameResult {
        let s = toml::to_vec(self)?;
        file.write_all(&s)?;
        Ok(())
    }

    /// Sets the window mode
    #[must_use]
    pub fn window_mode(mut self, window_mode: WindowMode) -> Self {
        self.window_mode = window_mode;
        self
    }

    /// Sets the backend
    #[must_use]
    pub fn backend(mut self, backend: Backend) -> Self {
        self.backend = backend;
        self
    }
}

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

    /// Tries to encode and decode a `Conf` object
    /// and makes sure it gets the same result it had.
    #[test]
    fn headless_encode_round_trip() {
        let c1 = conf::Conf::new();
        let mut writer = Vec::new();
        let _c = c1.to_toml_file(&mut writer).unwrap();
        let mut reader = writer.as_slice();
        let c2 = conf::Conf::from_toml_file(&mut reader).unwrap();
        assert_eq!(c1, c2);
    }
}
<|endoftext|>