<commit_before>use core::libc::size_t;
use core::ptr::{null, to_mut_unsafe_ptr};
use core::cast::transmute;
use ll::*;

fn require_ok(code: lwc_error) {
    match code {
        lwc_error_ok => (),
        e => fail fmt!("lwc error: %?", e)
    }
}

pub struct LwcStringRef {
    priv string: *lwc_string,

    drop {
        rust_lwc_string_unref(self.string);
    }
}

pub fn from_rust_string(s: &str) -> LwcStringRef {
    let mut interned_string = null();
    do str::as_c_str(s) |cs| {
        let code = lwc_intern_string(cs, s.len() as size_t, to_mut_unsafe_ptr(&mut interned_string));
        require_ok(code);
    }

    assert interned_string.is_not_null();

    LwcStringRef {
        string: interned_string
    }
}

pub fn from_lwc_string(s: *lwc_string) -> LwcStringRef {
    rust_lwc_string_ref(s);
    LwcStringRef {
        string: s
    }
}

impl LwcStringRef {
    fn len() -> uint {
        unsafe {
            (*self.string).len as uint
        }
    }

    fn clone() -> LwcStringRef {
        from_lwc_string(self.string)
    }
}

impl LwcStringRef: ToStr {
    pure fn to_str() -> ~str {
        unsafe {
            // The string is located immediately after the handle
            let p: *c_char = transmute(self.string.offset(1));
            str::raw::from_c_str_len(p, self.len())
        }
    }
}

#[test]
fn smoke_test() {
    let s1 = from_rust_string("test");
    let s2 = from_lwc_string(s1.string);
    debug!("%?", s1.len());
    debug!("%?", s2.len());
    assert s2.len() == 4;
    let s3 = s2.to_str();
    assert s3 == ~"test";
}

#[test]
fn multithreading_test() {
    for uint::range(0, 100) |i| {
        for iter::repeat(50) {
            do task::spawn {
                let s = vec::from_elem(i + 1, 0).to_str();
                let s = from_rust_string(s);
                let _t = s.clone();
            }
        }
    }
}<commit_msg>Add a way to get a raw lwc_string from an LwcStringRef<commit_after>use core::libc::size_t;
use core::ptr::{null, to_mut_unsafe_ptr};
use core::cast::transmute;
use ll::*;

fn require_ok(code: lwc_error) {
    match code {
        lwc_error_ok => (),
        e => fail fmt!("lwc error: %?", e)
    }
}

pub struct LwcStringRef {
    priv string: *lwc_string,

    drop {
        rust_lwc_string_unref(self.string);
    }
}

pub fn from_rust_string(s: &str) -> LwcStringRef {
    let mut interned_string = null();
    do str::as_c_str(s) |cs| {
        let code = lwc_intern_string(cs, s.len() as size_t, to_mut_unsafe_ptr(&mut interned_string));
        require_ok(code);
    }

    assert interned_string.is_not_null();

    LwcStringRef {
        string: interned_string
    }
}

pub fn from_lwc_string(s: *lwc_string) -> LwcStringRef {
    rust_lwc_string_ref(s);
    LwcStringRef {
        string: s
    }
}

impl LwcStringRef {
    fn len() -> uint {
        unsafe {
            (*self.string).len as uint
        }
    }

    fn clone() -> LwcStringRef {
        from_lwc_string(self.string)
    }

    fn raw_reffed() -> *lwc_string {
        self.string
    }
}

impl LwcStringRef: ToStr {
    pure fn to_str() -> ~str {
        unsafe {
            // The string is located immediately after the handle
            let p: *c_char = transmute(self.string.offset(1));
            str::raw::from_c_str_len(p, self.len())
        }
    }
}

#[test]
fn smoke_test() {
    let s1 = from_rust_string("test");
    let s2 = from_lwc_string(s1.string);
    debug!("%?", s1.len());
    debug!("%?", s2.len());
    assert s2.len() == 4;
    let s3 = s2.to_str();
    assert s3 == ~"test";
}

#[test]
fn multithreading_test() {
    for uint::range(0, 100) |i| {
        for iter::repeat(50) {
            do task::spawn {
                let s = vec::from_elem(i + 1, 0).to_str();
                let s = from_rust_string(s);
                let _t = s.clone();
            }
        }
    }
}<|endoftext|>


<commit_before>//! REPL for the gluon programming language
#![doc(html_root_url = "https://docs.rs/gluon_repl/0.13.1")] // # GLUON

#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate gluon_vm;
#[macro_use]
extern crate gluon_codegen;

use std::{
    ffi::OsStr,
    fs,
    io::{self, Write},
    path::{Path, PathBuf},
    sync::Arc,
};

use codespan_reporting::term::termcolor;
use quick_error::quick_error;
use structopt::StructOpt;
use walkdir::WalkDir;

use gluon::{base, parser, vm};

use crate::base::{
    filename_to_module,
    source::{self, Source},
};

use gluon::{
    new_vm_async, vm::thread::ThreadInternal, vm::Error as VMError, Result, Thread, ThreadExt,
};

mod repl;

quick_error! {
/// Error type wrapping all possible errors that can be generated from gluon
#[derive(Debug)]
#[allow(deprecated)]
enum Error {
    Failure(err: anyhow::Error) {
        display("{}", err)
        from()
    }
    Gluon(err: gluon::Error) {
        display("{}", err)
        from()
    }
}
}

const APP_INFO: app_dirs::AppInfo = app_dirs::AppInfo {
    name: "gluon-repl",
    author: "gluon-lang",
};

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, VmType, Getable, Pushable)]
pub enum Color {
    Auto,
    Always,
    AlwaysAnsi,
    Never,
}

impl Into<termcolor::ColorChoice> for Color {
    fn into(self) -> termcolor::ColorChoice {
        use termcolor::ColorChoice::*;
        match self {
            Color::Auto => Auto,
            Color::Always => Always,
            Color::AlwaysAnsi => AlwaysAnsi,
            Color::Never => Never,
        }
    }
}

impl Default for Color {
    fn default() -> Color {
        Color::Auto
    }
}

impl ::std::str::FromStr for Color {
    type Err = &'static str;
    fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
        use crate::Color::*;
        Ok(match s {
            "auto" => Auto,
            "always" => Always,
            "always-ansi" => AlwaysAnsi,
            "never" => Never,
            _ => return Err("Expected one of 'auto', 'always', 'always-ansi', 'never'"),
        })
    }
}

#[derive(StructOpt)]
#[structopt(about = "Formats gluon source code")]
pub struct FmtOpt {
    #[structopt(name = "FILE", parse(from_os_str), help = "Formats each file")]
    input: Vec<PathBuf>,
}

#[derive(StructOpt)]
pub enum SubOpt {
    #[structopt(name = "fmt", about = "Formats gluon source code")]
    Fmt(FmtOpt),
    #[structopt(name = "doc", about = "Documents gluon source code")]
    Doc(::gluon_doc::Opt),
}

const LONG_VERSION: &str = concat!(clap::crate_version!(), "\n", "commit: ", env!("GIT_HASH"));

#[derive(StructOpt)]
#[structopt(about = "executes gluon programs", long_version = LONG_VERSION)]
pub struct Opt {
    #[structopt(short = "i", long = "interactive", help = "Starts the repl")]
    interactive: bool,

    #[structopt(
        long = "color",
        default_value = "auto",
        help = "Coloring: auto, always, always-ansi, never"
    )]
    color: Color,

    #[structopt(
        long = "prompt",
        short = "p",
        default_value = "> ",
        help = "String printed as the prompt for the repl"
    )]
    prompt: String,

    #[structopt(
        long = "debug",
        default_value = "none",
        help = "Debug Level: none, low, high"
    )]
    debug_level: base::DebugLevel,

    #[structopt(
        long = "no-std",
        help = "Skip searching the internal standard library for requested modules."
    )]
    no_std: bool,

    #[structopt(name = "FILE", help = "Executes each file as a gluon program")]
    input: Vec<String>,

    #[structopt(
        last = true,
        name = "ARGS",
        help = "Extra arguments passed to the gluon program"
    )]
    #[allow(dead_code)]
    args: Vec<String>,

    #[structopt(subcommand)]
    subcommand_opt: Option<SubOpt>,
}

async fn run_files<I>(vm: &Thread, files: I) -> Result<()>
where
    I: IntoIterator,
    I::Item: AsRef<str>,
{
    for file in files {
        vm.load_file_async(file.as_ref()).await?;
    }
    Ok(())
}

#[cfg(feature = "env_logger")]
fn init_env_logger() {
    let _ = ::env_logger::try_init();
}

#[cfg(not(feature = "env_logger"))]
fn init_env_logger() {}

async fn format(file: &str, file_map: Arc<source::FileMap>, opt: &Opt) -> Result<String> {
    let thread = new_vm_async().await;
    thread.get_database_mut().use_standard_lib(!opt.no_std);

    Ok(thread
        .format_expr_async(
            &mut gluon_format::Formatter::default(),
            file,
            file_map.src(),
        )
        .await?)
}

async fn fmt_file(name: &Path, opt: &Opt) -> Result<()> {
    use std::fs::File;
    use std::io::Read;

    let mut buffer = String::new();
    {
        let mut input_file = File::open(name)?;
        input_file.read_to_string(&mut buffer)?;
    }

    let module_name = filename_to_module(&name.display().to_string());
    let mut code_map = source::CodeMap::new();
    let file_map = code_map.add_filemap(module_name.clone().into(), buffer);
    let formatted = format(&module_name, file_map.clone(), opt).await?;

    // Avoid touching the .glu file if it did not change
    if file_map.src() != formatted {
        let bk_name = name.with_extension("glu.bk");
        let tmp_name = name.with_extension("tmp");
        {
            let mut backup = File::create(&*bk_name)?;
            backup.write_all(formatted.as_bytes())?;
        }
        fs::rename(name, tmp_name)?;
        fs::rename(bk_name, name)?;
    }
    Ok(())
}

async fn fmt_stdio(opt: &Opt) -> Result<()> {
    use std::io::{stdin, stdout, Read};

    let mut buffer = String::new();
    stdin().read_to_string(&mut buffer)?;

    let mut code_map = source::CodeMap::new();
    let file_map = code_map.add_filemap("STDIN".into(), buffer);

    let formatted = format("STDIN", file_map, opt).await?;
    stdout().write_all(formatted.as_bytes())?;
    Ok(())
}

async fn run(opt: &Opt, color: Color, vm: &Thread) -> std::result::Result<(), Error> {
    vm.global_env().set_debug_level(opt.debug_level.clone());
    match opt.subcommand_opt {
        Some(SubOpt::Fmt(ref fmt_opt)) => {
            if !fmt_opt.input.is_empty() {
                let mut gluon_files = fmt_opt
                    .input
                    .iter()
                    .flat_map(|arg| {
                        WalkDir::new(arg).into_iter().filter_map(|entry| {
                            entry.ok().and_then(|entry| {
                                if entry.file_type().is_file()
                                    && entry.path().extension() == Some(OsStr::new("glu"))
                                {
                                    Some(entry.path().to_owned())
                                } else {
                                    None
                                }
                            })
                        })
                    })
                    .collect::<Vec<_>>();
                gluon_files.sort();
                gluon_files.dedup();

                for file in gluon_files {
                    fmt_file(&file, opt).await?;
                }
            } else {
                fmt_stdio(opt).await?;
            }
        }
        Some(SubOpt::Doc(ref doc_opt)) => {
            let input = &doc_opt.input;
            let output = &doc_opt.output;
            let thread = new_vm_async().await;
            gluon_doc::generate_for_path(&thread, input, output)?;
        }
        None => {
            if opt.interactive {
                let prompt = opt.prompt.clone();
                let debug_level = opt.debug_level.clone();
                let use_std_lib = !opt.no_std;
                repl::run(color, &prompt, debug_level, use_std_lib).await?;
            } else if !opt.input.is_empty() {
                run_files(&vm, &opt.input).await?;
            } else {
                writeln!(io::stderr(), "{}", Opt::clap().get_matches().usage())
                    .expect("Error writing help to stderr");
            }
        }
    }
    Ok(())
}

#[tokio::main(basic_scheduler)]
async fn main() {
    init_env_logger();

    let opt = Opt::from_args();

    let vm = new_vm_async().await;
    vm.get_database_mut()
        .use_standard_lib(!opt.no_std)
        .run_io(true);

    let color = opt.color;
    let result = run(&opt, opt.color, &vm).await;
    if let Err(err) = result {
        match err {
            Error::Gluon(gluon::Error::VM(VMError::Message(_))) => {
                eprintln!("{}\n{}", err, vm.context().stacktrace(0))
            }
            Error::Gluon(err) => {
                let mut stderr = termcolor::StandardStream::stderr(color.into());
                if let Err(err) = err.emit(&mut stderr) {
                    eprintln!("{}", err);
                } else {
                    eprintln!("");
                }
            }
            Error::Failure(err) => {
                eprintln!("{}", err);
            }
        }
        ::std::process::exit(1);
    }
}

#[cfg(test)]
mod tests {
    // If nothing else this suppresses the unused imports warnings when compiling in test mode
    #[test]
    fn execute_repl_help() {
        super::main();
    }
}
<commit_msg>perf: Avoid recreating the vm for each formatted file<commit_after>//! REPL for the gluon programming language
#![doc(html_root_url = "https://docs.rs/gluon_repl/0.13.1")] // # GLUON

#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate gluon_vm;
#[macro_use]
extern crate gluon_codegen;

use std::{
    ffi::OsStr,
    fs,
    io::{self, Write},
    path::{Path, PathBuf},
    sync::Arc,
};

use codespan_reporting::term::termcolor;
use quick_error::quick_error;
use structopt::StructOpt;
use walkdir::WalkDir;

use gluon::{base, parser, vm};

use crate::base::{
    filename_to_module,
    source::{self, Source},
};

use gluon::{
    new_vm_async, vm::thread::ThreadInternal, vm::Error as VMError, Result, Thread, ThreadExt,
};

mod repl;

quick_error! {
/// Error type wrapping all possible errors that can be generated from gluon
#[derive(Debug)]
#[allow(deprecated)]
enum Error {
    Failure(err: anyhow::Error) {
        display("{}", err)
        from()
    }
    Gluon(err: gluon::Error) {
        display("{}", err)
        from()
    }
}
}

const APP_INFO: app_dirs::AppInfo = app_dirs::AppInfo {
    name: "gluon-repl",
    author: "gluon-lang",
};

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, VmType, Getable, Pushable)]
pub enum Color {
    Auto,
    Always,
    AlwaysAnsi,
    Never,
}

impl Into<termcolor::ColorChoice> for Color {
    fn into(self) -> termcolor::ColorChoice {
        use termcolor::ColorChoice::*;
        match self {
            Color::Auto => Auto,
            Color::Always => Always,
            Color::AlwaysAnsi => AlwaysAnsi,
            Color::Never => Never,
        }
    }
}

impl Default for Color {
    fn default() -> Color {
        Color::Auto
    }
}

impl ::std::str::FromStr for Color {
    type Err = &'static str;
    fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
        use crate::Color::*;
        Ok(match s {
            "auto" => Auto,
            "always" => Always,
            "always-ansi" => AlwaysAnsi,
            "never" => Never,
            _ => return Err("Expected one of 'auto', 'always', 'always-ansi', 'never'"),
        })
    }
}

#[derive(StructOpt)]
#[structopt(about = "Formats gluon source code")]
pub struct FmtOpt {
    #[structopt(name = "FILE", parse(from_os_str), help = "Formats each file")]
    input: Vec<PathBuf>,
}

#[derive(StructOpt)]
pub enum SubOpt {
    #[structopt(name = "fmt", about = "Formats gluon source code")]
    Fmt(FmtOpt),
    #[structopt(name = "doc", about = "Documents gluon source code")]
    Doc(::gluon_doc::Opt),
}

const LONG_VERSION: &str = concat!(clap::crate_version!(), "\n", "commit: ", env!("GIT_HASH"));

#[derive(StructOpt)]
#[structopt(about = "executes gluon programs", long_version = LONG_VERSION)]
pub struct Opt {
    #[structopt(short = "i", long = "interactive", help = "Starts the repl")]
    interactive: bool,

    #[structopt(
        long = "color",
        default_value = "auto",
        help = "Coloring: auto, always, always-ansi, never"
    )]
    color: Color,

    #[structopt(
        long = "prompt",
        short = "p",
        default_value = "> ",
        help = "String printed as the prompt for the repl"
    )]
    prompt: String,

    #[structopt(
        long = "debug",
        default_value = "none",
        help = "Debug Level: none, low, high"
    )]
    debug_level: base::DebugLevel,

    #[structopt(
        long = "no-std",
        help = "Skip searching the internal standard library for requested modules."
    )]
    no_std: bool,

    #[structopt(name = "FILE", help = "Executes each file as a gluon program")]
    input: Vec<String>,

    #[structopt(
        last = true,
        name = "ARGS",
        help = "Extra arguments passed to the gluon program"
    )]
    #[allow(dead_code)]
    args: Vec<String>,

    #[structopt(subcommand)]
    subcommand_opt: Option<SubOpt>,
}

async fn run_files<I>(vm: &Thread, files: I) -> Result<()>
where
    I: IntoIterator,
    I::Item: AsRef<str>,
{
    for file in files {
        vm.load_file_async(file.as_ref()).await?;
    }
    Ok(())
}

#[cfg(feature = "env_logger")]
fn init_env_logger() {
    let _ = ::env_logger::try_init();
}

#[cfg(not(feature = "env_logger"))]
fn init_env_logger() {}

async fn format(thread: &Thread, file: &str, file_map: Arc<source::FileMap>) -> Result<String> {
    Ok(thread
        .format_expr_async(
            &mut gluon_format::Formatter::default(),
            file,
            file_map.src(),
        )
        .await?)
}

async fn fmt_file(thread: &Thread, name: &Path) -> Result<()> {
    use std::fs::File;
    use std::io::Read;

    let mut buffer = String::new();
    {
        let mut input_file = File::open(name)?;
        input_file.read_to_string(&mut buffer)?;
    }

    let module_name = filename_to_module(&name.display().to_string());
    let mut code_map = source::CodeMap::new();
    let file_map = code_map.add_filemap(module_name.clone().into(), buffer);
    let formatted = format(thread, &module_name, file_map.clone()).await?;

    // Avoid touching the .glu file if it did not change
    if file_map.src() != formatted {
        let bk_name = name.with_extension("glu.bk");
        let tmp_name = name.with_extension("tmp");
        {
            let mut backup = File::create(&*bk_name)?;
            backup.write_all(formatted.as_bytes())?;
        }
        fs::rename(name, tmp_name)?;
        fs::rename(bk_name, name)?;
    }
    Ok(())
}

async fn fmt_stdio(thread: &Thread) -> Result<()> {
    use std::io::{stdin, stdout, Read};

    let mut buffer = String::new();
    stdin().read_to_string(&mut buffer)?;

    let mut code_map = source::CodeMap::new();
    let file_map = code_map.add_filemap("STDIN".into(), buffer);

    let formatted = format(&thread, "STDIN", file_map).await?;
    stdout().write_all(formatted.as_bytes())?;
    Ok(())
}

async fn run(opt: &Opt, color: Color, vm: &Thread) -> std::result::Result<(), Error> {
    vm.global_env().set_debug_level(opt.debug_level.clone());
    match opt.subcommand_opt {
        Some(SubOpt::Fmt(ref fmt_opt)) => {
            let thread = new_vm_async().await;
            thread.get_database_mut().use_standard_lib(!opt.no_std);
            if !fmt_opt.input.is_empty() {
                let mut gluon_files = fmt_opt
                    .input
                    .iter()
                    .flat_map(|arg| {
                        WalkDir::new(arg).into_iter().filter_map(|entry| {
                            entry.ok().and_then(|entry| {
                                if entry.file_type().is_file()
                                    && entry.path().extension() == Some(OsStr::new("glu"))
                                {
                                    Some(entry.path().to_owned())
                                } else {
                                    None
                                }
                            })
                        })
                    })
                    .collect::<Vec<_>>();
                gluon_files.sort();
                gluon_files.dedup();

                for file in gluon_files {
                    fmt_file(&thread, &file).await?;
                }
            } else {
                fmt_stdio(&thread).await?;
            }
        }
        Some(SubOpt::Doc(ref doc_opt)) => {
            let input = &doc_opt.input;
            let output = &doc_opt.output;
            let thread = new_vm_async().await;
            gluon_doc::generate_for_path(&thread, input, output)?;
        }
        None => {
            if opt.interactive {
                let prompt = opt.prompt.clone();
                let debug_level = opt.debug_level.clone();
                let use_std_lib = !opt.no_std;
                repl::run(color, &prompt, debug_level, use_std_lib).await?;
            } else if !opt.input.is_empty() {
                run_files(&vm, &opt.input).await?;
            } else {
                writeln!(io::stderr(), "{}", Opt::clap().get_matches().usage())
                    .expect("Error writing help to stderr");
            }
        }
    }
    Ok(())
}

#[tokio::main(basic_scheduler)]
async fn main() {
    init_env_logger();

    let opt = Opt::from_args();

    let vm = new_vm_async().await;
    vm.get_database_mut()
        .use_standard_lib(!opt.no_std)
        .run_io(true);

    let color = opt.color;
    let result = run(&opt, opt.color, &vm).await;
    if let Err(err) = result {
        match err {
            Error::Gluon(gluon::Error::VM(VMError::Message(_))) => {
                eprintln!("{}\n{}", err, vm.context().stacktrace(0))
            }
            Error::Gluon(err) => {
                let mut stderr = termcolor::StandardStream::stderr(color.into());
                if let Err(err) = err.emit(&mut stderr) {
                    eprintln!("{}", err);
                } else {
                    eprintln!("");
                }
            }
            Error::Failure(err) => {
                eprintln!("{}", err);
            }
        }
        ::std::process::exit(1);
    }
}

#[cfg(test)]
mod tests {
    // If nothing else this suppresses the unused imports warnings when compiling in test mode
    #[test]
    fn execute_repl_help() {
        super::main();
    }
}
<|endoftext|>


<commit_before>use crate::methods::Method;
use eyre::Result;
use once_cell::sync::Lazy;
use schemars::{gen::SchemaSettings, JsonSchema};
use serde::Serialize;
use std::{
    any::type_name,
    collections::HashMap,
    sync::{atomic::AtomicBool, Mutex},
};
use thiserror::Error;

/// An enumeration of custom errors returned by this library
///
/// Where possible functions should return one of these errors to provide greater
/// context to the user, in particular regarding actions that can be taken to
/// resolve the error.
#[derive(Error, Debug, JsonSchema, Serialize)]
#[serde(tag = "type")]
#[schemars(deny_unknown_fields)]
pub enum Error {
    /// An identifer was supplied that does not match the pattern for the
    /// expected family of identifiers.
    #[error("Invalid universal identifier for family '{family}': {id}")]
    InvalidUUID { family: String, id: String },

    /// Used to indicate that two values are not the same (rather than
    /// return `false`, this error allows for convenient early return via `?`).
    #[error("Values are not the same (type and/or their value differ).")]
    NotSame,

    /// Used to indicate that two values are not equal (rather than
    /// return `false`, this error allows for convenient early return via `?`).
    #[error("Values are not equal (type is equal but their value differs")]
    NotEqual,

    /// The user attempted to apply a patch operation that is invalid for
    /// the type.
    #[error("Invalid patch operation '{op}' for type '{type_name}'")]
    InvalidPatchOperation { op: String, type_name: String },

    /// The user attempted to apply a patch operation with an invalid
    /// address for the type.
    #[error("Invalid patch address '{address}' for type '{type_name}'")]
    InvalidPatchAddress { address: String, type_name: String },

    /// The user attempted to use a slot with an invalid type for the
    /// type of the object (e.g. a `Slot::Name` on a `Vector`).
    #[error("Invalid slot type '{variant}' for type '{type_name}'")]
    InvalidSlotVariant { variant: String, type_name: String },

    /// The user attempted to use a slot with an invalid `Slot::Name`
    /// for the type (e.g a key for a `HashMap` that is not occupied).
    #[error("Invalid patch address name '{name}' for type '{type_name}'")]
    InvalidSlotName { name: String, type_name: String },

    /// The user attempted to use a slot with an invalid `Slot::Index`
    /// for the type (e.g an index that is greater than the size of a vector).
    #[error("Invalid patch address index '{index}' for type '{type_name}'")]
    InvalidSlotIndex { index: usize, type_name: String },

    /// The user attempted to apply a patch operation with an invalid
    /// value for the type.
    #[error("Invalid patch value for type '{type_name}'")]
    InvalidPatchValue { type_name: String },

    /// The user attempted to open a document with an unknown format
    #[error("Unknown format '{format}'")]
    UnknownFormat { format: String },

    /// The user attempted to call a method that is not implemented internally
    /// and so must be delegated to a plugin. However, none of the registered
    /// plugins implement this method. It may be that the user needs to do
    /// `stencila plugins refresh` to fetch the manifests for the plugins.
    /// Or it may be that the method name is just plain wrong.
    #[error("None of the registered plugins implement method '{method}'")]
    UndelegatableMethod { method: Method },

    /// The user attempted to call a method that is not implemented internally
    /// and so must be delegated to a plugin. At least one of the plugins implements
    /// this method but not with the values for the supplied parameters e.g.
    /// `decode(content, format)` which `format = 'some-unknown-format'`.
    #[error("None of the registered plugins implement method '{method}' with given parameters")]
    UndelegatableCall {
        method: Method,
        params: HashMap<String, serde_json::Value>,
    },

    /// The user attempted to call a method that is not implemented internally
    /// and so must be delegated to a plugin. There is a matching implementation
    /// for the call but plugin which implements it is not yet installed.
    #[error("Plugin '{plugin}' is not yet installed")]
    PluginNotInstalled { plugin: String },

    /// An error of unspecified type
    #[error("{message}")]
    Unspecified { message: String },
}

/// Create an `InvalidSlotType` error
pub fn invalid_slot_variant<Type>(variant: &str, object: Type) -> Error {
    Error::InvalidSlotVariant {
        variant: variant.into(),
        type_name: type_name::<Type>().into(),
    }
}

/// Create an `InvalidSlotName` error
pub fn invalid_slot_name<Type>(name: &str, object: Type) -> Error {
    Error::InvalidSlotName {
        name: name.into(),
        type_name: type_name::<Type>().into(),
    }
}

/// Create an `InvalidSlotIndex` error
pub fn invalid_slot_index<Type>(index: usize, object: Type) -> Error {
    Error::InvalidSlotIndex {
        index,
        type_name: type_name::<Type>().into(),
    }
}

/// A global stack of errors that can be used have "sideband" errors i.e. those that
/// do not cause a function to return early and are not part of the `Result` of a function.
pub static ERRORS: Lazy<Mutex<Vec<Error>>> = Lazy::new(|| Mutex::new(Vec::new()));

/// A flag to indicate if errors are being collected
static COLLECT: AtomicBool = AtomicBool::new(false);

/// Start collecting errors
pub fn start() {
    ERRORS.lock().expect("To be able to get lock").clear();
    COLLECT.swap(true, std::sync::atomic::Ordering::Relaxed);
}

/// Report an error
pub fn report(error: Error) {
    #[cfg(test)]
    eprintln!("Reported ERROR: {}", error);

    if COLLECT.load(std::sync::atomic::Ordering::Relaxed) {
        ERRORS.lock().expect("To be able to get lock").push(error);
    }
}

/// Record an error result if any
pub fn attempt<T>(result: Result<T>) {
    if let Err(error) = result {
        let message = error.to_string();
        match error.downcast::<Error>() {
            Ok(error) => report(error),
            Err(_) => report(Error::Unspecified { message }),
        }
    }
}

/// Stop collecting errors and return those collected
pub fn stop() -> Vec<Error> {
    COLLECT.swap(false, std::sync::atomic::Ordering::Relaxed);
    ERRORS.lock().expect("To be able to get lock").split_off(0)
}

/// Get JSON Schema for the `Error` enum.
///
/// This needs to be generated slightly differently to the other schemas
/// in this library because of the way `thiserror` wants enums to be layed out.
pub fn schema() -> Result<serde_json::Value> {
    let settings = SchemaSettings::draft2019_09().with(|settings| {
        settings.option_add_null_type = false;
        settings.inline_subschemas = false;
    });
    let schema = settings.into_generator().into_root_schema_for::<Error>();
    let schema = serde_json::to_value(schema)?;
    Ok(schema)
}
<commit_msg>chore(Errors): Linting<commit_after>use crate::methods::Method;
use eyre::Result;
use once_cell::sync::Lazy;
use schemars::{gen::SchemaSettings, JsonSchema};
use serde::Serialize;
use std::{
    any::type_name,
    collections::HashMap,
    sync::{atomic::AtomicBool, Mutex},
};
use thiserror::Error;

/// An enumeration of custom errors returned by this library
///
/// Where possible functions should return one of these errors to provide greater
/// context to the user, in particular regarding actions that can be taken to
/// resolve the error.
#[derive(Error, Debug, JsonSchema, Serialize)]
#[serde(tag = "type")]
#[schemars(deny_unknown_fields)]
pub enum Error {
    /// An identifer was supplied that does not match the pattern for the
    /// expected family of identifiers.
    #[error("Invalid universal identifier for family '{family}': {id}")]
    InvalidUUID { family: String, id: String },

    /// Used to indicate that two values are not the same (rather than
    /// return `false`, this error allows for convenient early return via `?`).
    #[error("Values are not the same (type and/or their value differ).")]
    NotSame,

    /// Used to indicate that two values are not equal (rather than
    /// return `false`, this error allows for convenient early return via `?`).
    #[error("Values are not equal (type is equal but their value differs")]
    NotEqual,

    /// The user attempted to apply a patch operation that is invalid for
    /// the type.
    #[error("Invalid patch operation '{op}' for type '{type_name}'")]
    InvalidPatchOperation { op: String, type_name: String },

    /// The user attempted to apply a patch operation with an invalid
    /// address for the type.
    #[error("Invalid patch address '{address}' for type '{type_name}'")]
    InvalidPatchAddress { address: String, type_name: String },

    /// The user attempted to use a slot with an invalid type for the
    /// type of the object (e.g. a `Slot::Name` on a `Vector`).
    #[error("Invalid slot type '{variant}' for type '{type_name}'")]
    InvalidSlotVariant { variant: String, type_name: String },

    /// The user attempted to use a slot with an invalid `Slot::Name`
    /// for the type (e.g a key for a `HashMap` that is not occupied).
    #[error("Invalid patch address name '{name}' for type '{type_name}'")]
    InvalidSlotName { name: String, type_name: String },

    /// The user attempted to use a slot with an invalid `Slot::Index`
    /// for the type (e.g an index that is greater than the size of a vector).
    #[error("Invalid patch address index '{index}' for type '{type_name}'")]
    InvalidSlotIndex { index: usize, type_name: String },

    /// The user attempted to apply a patch operation with an invalid
    /// value for the type.
    #[error("Invalid patch value for type '{type_name}'")]
    InvalidPatchValue { type_name: String },

    /// The user attempted to open a document with an unknown format
    #[error("Unknown format '{format}'")]
    UnknownFormat { format: String },

    /// The user attempted to call a method that is not implemented internally
    /// and so must be delegated to a plugin. However, none of the registered
    /// plugins implement this method. It may be that the user needs to do
    /// `stencila plugins refresh` to fetch the manifests for the plugins.
    /// Or it may be that the method name is just plain wrong.
    #[error("None of the registered plugins implement method '{method}'")]
    UndelegatableMethod { method: Method },

    /// The user attempted to call a method that is not implemented internally
    /// and so must be delegated to a plugin. At least one of the plugins implements
    /// this method but not with the values for the supplied parameters e.g.
    /// `decode(content, format)` which `format = 'some-unknown-format'`.
    #[error("None of the registered plugins implement method '{method}' with given parameters")]
    UndelegatableCall {
        method: Method,
        params: HashMap<String, serde_json::Value>,
    },

    /// The user attempted to call a method that is not implemented internally
    /// and so must be delegated to a plugin. There is a matching implementation
    /// for the call but plugin which implements it is not yet installed.
    #[error("Plugin '{plugin}' is not yet installed")]
    PluginNotInstalled { plugin: String },

    /// An error of unspecified type
    #[error("{message}")]
    Unspecified { message: String },
}

/// Create an `InvalidSlotType` error
pub fn invalid_slot_variant<Type>(variant: &str, _object: Type) -> Error {
    Error::InvalidSlotVariant {
        variant: variant.into(),
        type_name: type_name::<Type>().into(),
    }
}

/// Create an `InvalidSlotName` error
pub fn invalid_slot_name<Type>(name: &str, _object: Type) -> Error {
    Error::InvalidSlotName {
        name: name.into(),
        type_name: type_name::<Type>().into(),
    }
}

/// Create an `InvalidSlotIndex` error
pub fn invalid_slot_index<Type>(index: usize, _object: Type) -> Error {
    Error::InvalidSlotIndex {
        index,
        type_name: type_name::<Type>().into(),
    }
}

/// A global stack of errors that can be used have "sideband" errors i.e. those that
/// do not cause a function to return early and are not part of the `Result` of a function.
pub static ERRORS: Lazy<Mutex<Vec<Error>>> = Lazy::new(|| Mutex::new(Vec::new()));

/// A flag to indicate if errors are being collected
static COLLECT: AtomicBool = AtomicBool::new(false);

/// Start collecting errors
pub fn start() {
    ERRORS.lock().expect("To be able to get lock").clear();
    COLLECT.swap(true, std::sync::atomic::Ordering::Relaxed);
}

/// Report an error
pub fn report(error: Error) {
    #[cfg(test)]
    eprintln!("Reported ERROR: {}", error);

    if COLLECT.load(std::sync::atomic::Ordering::Relaxed) {
        ERRORS.lock().expect("To be able to get lock").push(error);
    }
}

/// Record an error result if any
pub fn attempt<T>(result: Result<T>) {
    if let Err(error) = result {
        let message = error.to_string();
        match error.downcast::<Error>() {
            Ok(error) => report(error),
            Err(_) => report(Error::Unspecified { message }),
        }
    }
}

/// Stop collecting errors and return those collected
pub fn stop() -> Vec<Error> {
    COLLECT.swap(false, std::sync::atomic::Ordering::Relaxed);
    ERRORS.lock().expect("To be able to get lock").split_off(0)
}

/// Get JSON Schema for the `Error` enum.
///
/// This needs to be generated slightly differently to the other schemas
/// in this library because of the way `thiserror` wants enums to be layed out.
pub fn schema() -> Result<serde_json::Value> {
    let settings = SchemaSettings::draft2019_09().with(|settings| {
        settings.option_add_null_type = false;
        settings.inline_subschemas = false;
    });
    let schema = settings.into_generator().into_root_schema_for::<Error>();
    let schema = serde_json::to_value(schema)?;
    Ok(schema)
}
<|endoftext|>


<commit_before>extern crate bio;
extern crate argparse;
use std::io;
use std::cmp;
use bio::io::fasta;
use std::fmt;
use std::fs::File;
use argparse::{ArgumentParser, StoreTrue, Store, Print, Collect};

struct FastaStats {
   min_len: u64,
   average_len: u64,
   max_len: u64,
   total: u64,
   num_seqs: u64,
}

impl FastaStats {
   pub fn new<R: io::Read>(options: &Options, reader: R) -> Option<FastaStats> {
      let fasta_reader = fasta::Reader::new(reader);
      let mut num_seqs:u64 = 0;
      let mut total:u64 = 0;
      let mut max_len:u64 = 0;
      let mut min_len:u64 = 0;
      let mut this_len:u64;

      for next in fasta_reader.records() {
         match next {
            Ok(record) => {
               this_len = record.seq().len() as u64;
               if this_len >= options.minlen {
                  num_seqs += 1;
                  total += this_len;
                  if num_seqs == 1 {
                     max_len = this_len;
                     min_len = this_len;
                  }
                  else {
                     max_len = cmp::max(max_len, this_len);
                     min_len = cmp::min(min_len, this_len);
                  }
               }
            },
            // XXX handle errors properly
            Err(error) => println!("{}", error),
         }
      }
      if num_seqs > 0 {
         // XXX check whether integer division does floor
         let average_len = ((total as f64) / (num_seqs as f64)).floor() as u64;
         Some(FastaStats { min_len: min_len,
                           average_len: average_len,
                           max_len: max_len,
                           total: total,
                           num_seqs: num_seqs })
      }
      else {
         None
      }
   }
}

impl fmt::Display for FastaStats {
   fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
      write!(f, "{}\t{}\t{}\t{}\t{}", self.num_seqs, self.total,
         self.min_len, self.average_len, self.max_len)
   }
}

struct Options {
    verbose: bool,
    minlen: u64,
    fasta_files: Vec<String>,
}

fn parse_options() -> Options {
   let mut options = Options { verbose: false, minlen: 0, fasta_files: Vec::new() };
   {  let mut ap = ArgumentParser::new();
       ap.set_description("Print fasta stats");
       ap.refer(&mut options.verbose)
           .add_option(&["-v", "--verbose"], StoreTrue,
           "Print more stuff about what's happening");
       ap.refer(&mut options.minlen)
           .add_option(&["--minlen"], Store,
           "Minimum length sequence to include in stats");
       ap.refer(&mut options.fasta_files)
           .add_argument(&"fasta_files", Collect,
           "Input FASTA files");
       ap.add_option(&["--version"],
          Print(env!("CARGO_PKG_VERSION").to_string()), "Show version");
       ap.parse_args_or_exit();
   }
   return options;
}

fn main() {
   let options = parse_options();
   println!("FILENAME\tTOTAL\tNUMSEQ\tMIN\tAVG\tMAX");
   for filename in &options.fasta_files {
      match File::open(filename) {
         Ok(file) => {
            match FastaStats::new(&options, file) {
               Some(stats) => {
                  println!("{}\t{}", filename, stats);
               },
               None => {
                  println!("{}\t0\t0\t-\t-\t-", filename);
               }
            }
         },
         Err(e) => {
            // XXX handle errors properly
            println!("{}", e);
         }
      }
   }
}
<commit_msg>If no FASTA files specified on command line then read from stdin<commit_after>extern crate bio;
extern crate argparse;
use std::io;
use std::cmp;
use bio::io::fasta;
use std::fmt;
use std::fs::File;
use argparse::{ArgumentParser, StoreTrue, Store, Print, Collect};

struct FastaStats {
   min_len: u64,
   average_len: u64,
   max_len: u64,
   total: u64,
   num_seqs: u64,
}

impl FastaStats {
   pub fn new<R: io::Read>(options: &Options, reader: R) -> Option<FastaStats> {
      let fasta_reader = fasta::Reader::new(reader);
      let mut num_seqs:u64 = 0;
      let mut total:u64 = 0;
      let mut max_len:u64 = 0;
      let mut min_len:u64 = 0;
      let mut this_len:u64;

      for next in fasta_reader.records() {
         match next {
            Ok(record) => {
               this_len = record.seq().len() as u64;
               if this_len >= options.minlen {
                  num_seqs += 1;
                  total += this_len;
                  if num_seqs == 1 {
                     max_len = this_len;
                     min_len = this_len;
                  }
                  else {
                     max_len = cmp::max(max_len, this_len);
                     min_len = cmp::min(min_len, this_len);
                  }
               }
            },
            // XXX handle errors properly
            Err(error) => println!("{}", error),
         }
      }
      if num_seqs > 0 {
         // XXX check whether integer division does floor
         let average_len = ((total as f64) / (num_seqs as f64)).floor() as u64;
         Some(FastaStats { min_len: min_len,
                           average_len: average_len,
                           max_len: max_len,
                           total: total,
                           num_seqs: num_seqs })
      }
      else {
         None
      }
   }
}

impl fmt::Display for FastaStats {
   fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
      write!(f, "{}\t{}\t{}\t{}\t{}", self.num_seqs, self.total,
         self.min_len, self.average_len, self.max_len)
   }
}

struct Options {
    verbose: bool,
    minlen: u64,
    fasta_files: Vec<String>,
}

fn parse_options() -> Options {
   let mut options = Options { verbose: false, minlen: 0, fasta_files: Vec::new() };
   {  let mut ap = ArgumentParser::new();
       ap.set_description("Print fasta stats");
       ap.refer(&mut options.verbose)
           .add_option(&["-v", "--verbose"], StoreTrue,
           "Print more stuff about what's happening");
       ap.refer(&mut options.minlen)
           .add_option(&["--minlen"], Store,
           "Minimum length sequence to include in stats");
       ap.refer(&mut options.fasta_files)
           .add_argument(&"fasta_files", Collect,
           "Input FASTA files");
       ap.add_option(&["--version"],
          Print(env!("CARGO_PKG_VERSION").to_string()), "Show version");
       ap.parse_args_or_exit();
   }
   return options;
}

fn compute_print_stats<R: io::Read>(options: &Options, filename: &String, reader: R) -> () {
   match FastaStats::new(options, reader) {
      Some(stats) => {
         println!("{}\t{}", filename, stats);
      },
      None => {
         println!("{}\t0\t0\t-\t-\t-", filename);
      }
   }
}

fn main() {
   let options = parse_options();
   println!("FILENAME\tTOTAL\tNUMSEQ\tMIN\tAVG\tMAX");
   if options.fasta_files.len() == 0 {
      compute_print_stats(&options, &String::from("stdin"), io::stdin());
   }
   else {
      for filename in &options.fasta_files {
         match File::open(filename) {
            Ok(file) => {
               compute_print_stats(&options, filename, file);
            },
            Err(e) => {
               // XXX handle errors properly
               println!("{}", e);
            }
         }
      }
   }
}
<|endoftext|>


<commit_before>//! Implementation of the top level `*Event` derive macro.

use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{Data, DataStruct, DeriveInput, Field, Fields, FieldsNamed, Ident};

use crate::event_parse::{to_kind_variation, EventKind, EventKindVariation};

/// Derive `Event` macro code generation.
pub fn expand_event(input: DeriveInput) -> syn::Result<TokenStream> {
    let ident = &input.ident;
    let (kind, var) = to_kind_variation(ident).ok_or_else(|| {
        syn::Error::new(Span::call_site(), "not a valid ruma event struct identifier")
    })?;

    let fields = if let Data::Struct(DataStruct { fields, .. }) = input.data.clone() {
        if let Fields::Named(FieldsNamed { named, .. }) = fields {
            if !named.iter().any(|f| f.ident.as_ref().unwrap() == "content") {
                return Err(syn::Error::new(
                    Span::call_site(),
                    "struct must contain a `content` field",
                ));
            }

            named.into_iter().collect::<Vec<_>>()
        } else {
            return Err(syn::Error::new_spanned(
                fields,
                "the `Event` derive only supports named fields",
            ));
        }
    } else {
        return Err(syn::Error::new_spanned(
            input.ident,
            "the `Event` derive only supports structs with named fields",
        ));
    };

    let serialize_impl = expand_serialize_event(&input, &var, &fields)?;

    let deserialize_impl = expand_deserialize_event(&input, &var, &fields)?;

    let conversion_impl = expand_from_into(&input, &kind, &var, &fields, &import_path);

    let eq_impl = expand_eq_ord_event(&input, &fields);

    Ok(quote! {
        #conversion_impl

        #serialize_impl

        #deserialize_impl

        #eq_impl
    })
}

fn expand_serialize_event(
    input: &DeriveInput,
    var: &EventKindVariation,
    fields: &[Field],
) -> syn::Result<TokenStream> {
    let ident = &input.ident;
    let (impl_gen, ty_gen, where_clause) = input.generics.split_for_impl();
    let serialize_fields = fields
        .iter()
        .map(|field| {
            let name = field.ident.as_ref().unwrap();
            if name == "content" && var.is_redacted() {
                quote! {
                    if ::ruma_events::RedactedEventContent::has_serialize_fields(&self.content) {
                        state.serialize_field("content", &self.content)?;
                    }
                }
            } else if name == "prev_content" {
                quote! {
                    if let Some(content) = self.prev_content.as_ref() {
                        state.serialize_field("prev_content", content)?;
                    }
                }
            } else if name == "origin_server_ts" {
                quote! {
                    let time_since_epoch =
                        self.origin_server_ts.duration_since(::std::time::UNIX_EPOCH).unwrap();

                    let timestamp = <::js_int::UInt as ::std::convert::TryFrom<_>>::try_from(
                        time_since_epoch.as_millis(),
                    ).map_err(S::Error::custom)?;

                    state.serialize_field("origin_server_ts", &timestamp)?;
                }
            } else if name == "unsigned" {
                quote! {
                    if !self.unsigned.is_empty() {
                        state.serialize_field("unsigned", &self.unsigned)?;
                    }
                }
            } else {
                quote! {
                    state.serialize_field(stringify!(#name), &self.#name)?;
                }
            }
        })
        .collect::<Vec<_>>();

    Ok(quote! {
        impl #impl_gen ::serde::ser::Serialize for #ident #ty_gen #where_clause {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where
                S: ::serde::ser::Serializer,
            {
                use ::serde::ser::{SerializeStruct as _, Error as _};

                let event_type = ::ruma_events::EventContent::event_type(&self.content);

                let mut state = serializer.serialize_struct(stringify!(#ident), 7)?;

                state.serialize_field("type", event_type)?;
                #( #serialize_fields )*
                state.end()
            }
        }
    })
}

fn expand_deserialize_event(
    input: &DeriveInput,
    var: &EventKindVariation,
    fields: &[Field],
) -> syn::Result<TokenStream> {
    let ident = &input.ident;
    // we know there is a content field already
    let content_type = fields
        .iter()
        // we also know that the fields are named and have an ident
        .find(|f| f.ident.as_ref().unwrap() == "content")
        .map(|f| f.ty.clone())
        .unwrap();

    let (impl_generics, ty_gen, where_clause) = input.generics.split_for_impl();
    let is_generic = !input.generics.params.is_empty();

    let enum_variants = fields
        .iter()
        .map(|field| {
            let name = field.ident.as_ref().unwrap();
            to_camel_case(name)
        })
        .collect::<Vec<_>>();

    let deserialize_var_types = fields
        .iter()
        .map(|field| {
            let name = field.ident.as_ref().unwrap();
            let ty = &field.ty;
            if name == "content" || name == "prev_content" {
                if is_generic {
                    quote! { Box<::serde_json::value::RawValue> }
                } else {
                    quote! { #content_type }
                }
            } else if name == "origin_server_ts" {
                quote! { ::js_int::UInt }
            } else {
                quote! { #ty }
            }
        })
        .collect::<Vec<_>>();

    let ok_or_else_fields = fields
    .iter()
    .map(|field| {
        let name = field.ident.as_ref().unwrap();
        if name == "content" {
            if is_generic && var.is_redacted() {
                quote! {
                    let content = match C::has_deserialize_fields() {
                        ::ruma_events::HasDeserializeFields::False => {
                            C::empty(&event_type).map_err(A::Error::custom)?
                        },
                        ::ruma_events::HasDeserializeFields::True => {
                            let json = content.ok_or_else(
                                || ::serde::de::Error::missing_field("content"),
                            )?;
                            C::from_parts(&event_type, json).map_err(A::Error::custom)?
                        },
                        ::ruma_events::HasDeserializeFields::Optional => {
                            let json = content.unwrap_or(
                                ::serde_json::value::RawValue::from_string("{}".to_string())
                                    .unwrap()
                            );
                            C::from_parts(&event_type, json).map_err(A::Error::custom)?
                        },
                    };
                }
            } else if is_generic {
                quote! {
                    let json = content.ok_or_else(|| ::serde::de::Error::missing_field("content"))?;
                    let content = C::from_parts(&event_type, json).map_err(A::Error::custom)?;
                }
            } else {
                quote! {
                    let content = content.ok_or_else(
                        || ::serde::de::Error::missing_field("content"),
                    )?;
                }
            }
        } else if name == "prev_content" {
            if is_generic {
                quote! {
                    let prev_content = if let Some(json) = prev_content {
                        Some(C::from_parts(&event_type, json).map_err(A::Error::custom)?)
                    } else {
                        None
                    };
                }
            } else {
                quote! {
                    let prev_content = if let Some(content) = prev_content {
                        Some(content)
                    } else {
                        None
                    };
                }
            }
        } else if name == "origin_server_ts" {
            quote! {
                let origin_server_ts = origin_server_ts
                    .map(|time| {
                        let t = time.into();
                        ::std::time::UNIX_EPOCH + ::std::time::Duration::from_millis(t)
                    })
                    .ok_or_else(|| ::serde::de::Error::missing_field("origin_server_ts"))?;
            }
        } else if name == "unsigned" {
            quote! { let unsigned = unsigned.unwrap_or_default(); }
        } else {
            quote! {
                let #name = #name.ok_or_else(|| {
                    ::serde::de::Error::missing_field(stringify!(#name))
                })?;
            }
        }
    })
    .collect::<Vec<_>>();

    let field_names = fields.iter().flat_map(|f| &f.ident).collect::<Vec<_>>();

    let deserialize_impl_gen = if is_generic {
        let gen = &input.generics.params;
        quote! { <'de, #gen> }
    } else {
        quote! { <'de> }
    };
    let deserialize_phantom_type = if is_generic {
        quote! { ::std::marker::PhantomData }
    } else {
        quote! {}
    };

    Ok(quote! {
        impl #deserialize_impl_gen ::serde::de::Deserialize<'de> for #ident #ty_gen #where_clause {
            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
            where
                D: ::serde::de::Deserializer<'de>,
            {
                #[derive(::serde::Deserialize)]
                #[serde(field_identifier, rename_all = "snake_case")]
                enum Field {
                    // since this is represented as an enum we have to add it so the JSON picks it
                    // up
                    Type,
                    #( #enum_variants, )*
                    #[serde(other)]
                    Unknown,
                }

                /// Visits the fields of an event struct to handle deserialization of
                /// the `content` and `prev_content` fields.
                struct EventVisitor #impl_generics (#deserialize_phantom_type #ty_gen);

                impl #deserialize_impl_gen ::serde::de::Visitor<'de>
                    for EventVisitor #ty_gen #where_clause
                {
                    type Value = #ident #ty_gen;

                    fn expecting(
                        &self,
                        formatter: &mut ::std::fmt::Formatter<'_>,
                    ) -> ::std::fmt::Result {
                        write!(formatter, "struct implementing {}", stringify!(#content_type))
                    }

                    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
                    where
                        A: ::serde::de::MapAccess<'de>,
                    {
                        use ::serde::de::Error as _;

                        let mut event_type: Option<String> = None;
                        #( let mut #field_names: Option<#deserialize_var_types> = None; )*

                        while let Some(key) = map.next_key()? {
                            match key {
                                Field::Unknown => {
                                    let _: ::serde::de::IgnoredAny = map.next_value()?;
                                },
                                Field::Type => {
                                    if event_type.is_some() {
                                        return Err(::serde::de::Error::duplicate_field("type"));
                                    }
                                    event_type = Some(map.next_value()?);
                                }
                                #(
                                    Field::#enum_variants => {
                                        if #field_names.is_some() {
                                            return Err(::serde::de::Error::duplicate_field(
                                                stringify!(#field_names),
                                            ));
                                        }
                                        #field_names = Some(map.next_value()?);
                                    }
                                )*
                            }
                        }

                        let event_type =
                            event_type.ok_or_else(|| ::serde::de::Error::missing_field("type"))?;
                        #( #ok_or_else_fields )*

                        Ok(#ident {
                            #( #field_names ),*
                        })
                    }
                }

                deserializer.deserialize_map(EventVisitor(#deserialize_phantom_type))
            }
        }
    })
}

fn expand_from_into(
    input: &DeriveInput,
    kind: &EventKind,
    var: &EventKindVariation,
    fields: &[Field],
    import_path: &TokenStream,
) -> Option<TokenStream> {
    let ident = &input.ident;

    let (impl_generics, ty_gen, where_clause) = input.generics.split_for_impl();

    let fields = fields.iter().flat_map(|f| &f.ident).collect::<Vec<_>>();

    let fields_without_unsigned =
        fields.iter().filter(|id| id.to_string().as_str() != "unsigned").collect::<Vec<_>>();

    let (into, into_full_event) = if var.is_redacted() {
        (quote! { unsigned: unsigned.into(), }, quote! { unsigned: unsigned.into_full(room_id), })
    } else if kind == &EventKind::Ephemeral {
        (TokenStream::new(), TokenStream::new())
    } else {
        (quote! { unsigned, }, quote! { unsigned, })
    };

    if let EventKindVariation::Sync | EventKindVariation::RedactedSync = var {
        let full_struct = kind.to_event_ident(&var.to_full_variation());
        Some(quote! {
            impl #impl_generics From<#full_struct #ty_gen> for #ident #ty_gen #where_clause {
                fn from(event: #full_struct #ty_gen) -> Self {
                    let #full_struct {
                        #( #fields, )* ..
                    } = event;
                    Self { #( #fields_without_unsigned, )* #into }
                }
            }

            impl #impl_generics #ident #ty_gen #where_clause {
                /// Convert this sync event into a full event, one with a room_id field.
                pub fn into_full_event(
                    self,
                    room_id: #import_path::exports::ruma_identifiers::RoomId,
                ) -> #full_struct #ty_gen {
                    let Self { #( #fields, )* } = self;
                    #full_struct {
                        #( #fields_without_unsigned, )*
                        room_id: room_id.clone(),
                        #into_full_event
                    }
                }
            }
        })
    } else {
        None
    }
}

fn expand_eq_ord_event(input: &DeriveInput, fields: &[Field]) -> Option<TokenStream> {
    if fields.iter().flat_map(|f| f.ident.as_ref()).any(|f| f == "event_id") {
        let ident = &input.ident;
        let (impl_gen, ty_gen, where_clause) = input.generics.split_for_impl();

        Some(quote! {
            impl #impl_gen ::std::cmp::PartialEq for #ident #ty_gen #where_clause {
                /// This checks if two `EventId`s are equal.
                fn eq(&self, other: &Self) -> ::std::primitive::bool {
                    self.event_id == other.event_id
                }
            }

            impl #impl_gen ::std::cmp::Eq for #ident #ty_gen #where_clause {}

            impl #impl_gen ::std::cmp::PartialOrd for #ident #ty_gen #where_clause {
                /// This compares `EventId`s and orders them lexicographically.
                fn partial_cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
                    self.event_id.partial_cmp(&other.event_id)
                }
            }

            impl #impl_gen ::std::cmp::Ord for #ident #ty_gen #where_clause {
                /// This compares `EventId`s and orders them lexicographically.
                fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
                    self.event_id.cmp(&other.event_id)
                }
            }
        })
    } else {
        None
    }
}

/// CamelCase's a field ident like "foo_bar" to "FooBar".
fn to_camel_case(name: &Ident) -> Ident {
    let span = name.span();
    let name = name.to_string();

    let s = name
        .split('_')
        .map(|s| s.chars().next().unwrap().to_uppercase().to_string() + &s[1..])
        .collect::<String>();
    Ident::new(&s, span)
}
<commit_msg>Use proc_macro_crate name in the Event derive<commit_after>//! Implementation of the top level `*Event` derive macro.

use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{Data, DataStruct, DeriveInput, Field, Fields, FieldsNamed, Ident};

use crate::{
    event_parse::{to_kind_variation, EventKind, EventKindVariation},
    import_ruma_events,
};

/// Derive `Event` macro code generation.
pub fn expand_event(input: DeriveInput) -> syn::Result<TokenStream> {
    let import_path = import_ruma_events();

    let ident = &input.ident;
    let (kind, var) = to_kind_variation(ident).ok_or_else(|| {
        syn::Error::new(Span::call_site(), "not a valid ruma event struct identifier")
    })?;

    let fields = if let Data::Struct(DataStruct { fields, .. }) = input.data.clone() {
        if let Fields::Named(FieldsNamed { named, .. }) = fields {
            if !named.iter().any(|f| f.ident.as_ref().unwrap() == "content") {
                return Err(syn::Error::new(
                    Span::call_site(),
                    "struct must contain a `content` field",
                ));
            }

            named.into_iter().collect::<Vec<_>>()
        } else {
            return Err(syn::Error::new_spanned(
                fields,
                "the `Event` derive only supports named fields",
            ));
        }
    } else {
        return Err(syn::Error::new_spanned(
            input.ident,
            "the `Event` derive only supports structs with named fields",
        ));
    };

    let serialize_impl = expand_serialize_event(&input, &var, &fields, &import_path)?;

    let deserialize_impl = expand_deserialize_event(&input, &var, &fields, &import_path)?;

    let conversion_impl = expand_from_into(&input, &kind, &var, &fields, &import_path);

    let eq_impl = expand_eq_ord_event(&input, &fields);

    Ok(quote! {
        #conversion_impl

        #serialize_impl

        #deserialize_impl

        #eq_impl
    })
}

fn expand_serialize_event(
    input: &DeriveInput,
    var: &EventKindVariation,
    fields: &[Field],
    import_path: &TokenStream,
) -> syn::Result<TokenStream> {
    let ident = &input.ident;
    let (impl_gen, ty_gen, where_clause) = input.generics.split_for_impl();
    let serialize_fields = fields
        .iter()
        .map(|field| {
            let name = field.ident.as_ref().unwrap();
            if name == "content" && var.is_redacted() {
                quote! {
                    if #import_path::RedactedEventContent::has_serialize_fields(&self.content) {
                        state.serialize_field("content", &self.content)?;
                    }
                }
            } else if name == "prev_content" {
                quote! {
                    if let Some(content) = self.prev_content.as_ref() {
                        state.serialize_field("prev_content", content)?;
                    }
                }
            } else if name == "origin_server_ts" {
                quote! {
                    let time_since_epoch =
                        self.origin_server_ts.duration_since(::std::time::UNIX_EPOCH).unwrap();

                    let timestamp = <#import_path::exports::js_int::UInt as ::std::convert::TryFrom<_>>::try_from(
                        time_since_epoch.as_millis(),
                    ).map_err(S::Error::custom)?;

                    state.serialize_field("origin_server_ts", &timestamp)?;
                }
            } else if name == "unsigned" {
                quote! {
                    if !self.unsigned.is_empty() {
                        state.serialize_field("unsigned", &self.unsigned)?;
                    }
                }
            } else {
                quote! {
                    state.serialize_field(stringify!(#name), &self.#name)?;
                }
            }
        })
        .collect::<Vec<_>>();

    Ok(quote! {
        impl #impl_gen #import_path::exports::serde::ser::Serialize for #ident #ty_gen #where_clause {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where
                S: #import_path::exports::serde::ser::Serializer,
            {
                use #import_path::exports::serde::ser::{SerializeStruct as _, Error as _};

                let event_type = #import_path::EventContent::event_type(&self.content);

                let mut state = serializer.serialize_struct(stringify!(#ident), 7)?;

                state.serialize_field("type", event_type)?;
                #( #serialize_fields )*
                state.end()
            }
        }
    })
}

fn expand_deserialize_event(
    input: &DeriveInput,
    var: &EventKindVariation,
    fields: &[Field],
    import_path: &TokenStream,
) -> syn::Result<TokenStream> {
    let ident = &input.ident;
    // we know there is a content field already
    let content_type = fields
        .iter()
        // we also know that the fields are named and have an ident
        .find(|f| f.ident.as_ref().unwrap() == "content")
        .map(|f| f.ty.clone())
        .unwrap();

    let (impl_generics, ty_gen, where_clause) = input.generics.split_for_impl();
    let is_generic = !input.generics.params.is_empty();

    let enum_variants = fields
        .iter()
        .map(|field| {
            let name = field.ident.as_ref().unwrap();
            to_camel_case(name)
        })
        .collect::<Vec<_>>();

    let deserialize_var_types = fields
        .iter()
        .map(|field| {
            let name = field.ident.as_ref().unwrap();
            let ty = &field.ty;
            if name == "content" || name == "prev_content" {
                if is_generic {
                    quote! { Box<#import_path::exports::serde_json::value::RawValue> }
                } else {
                    quote! { #content_type }
                }
            } else if name == "origin_server_ts" {
                quote! { #import_path::exports::js_int::UInt }
            } else {
                quote! { #ty }
            }
        })
        .collect::<Vec<_>>();

    let ok_or_else_fields = fields
    .iter()
    .map(|field| {
        let name = field.ident.as_ref().unwrap();
        if name == "content" {
            if is_generic && var.is_redacted() {
                quote! {
                    let content = match C::has_deserialize_fields() {
                        #import_path::HasDeserializeFields::False => {
                            C::empty(&event_type).map_err(A::Error::custom)?
                        },
                        #import_path::HasDeserializeFields::True => {
                            let json = content.ok_or_else(
                                || #import_path::exports::serde::de::Error::missing_field("content"),
                            )?;
                            C::from_parts(&event_type, json).map_err(A::Error::custom)?
                        },
                        #import_path::HasDeserializeFields::Optional => {
                            let json = content.unwrap_or(
                                #import_path::exports::serde_json::value::RawValue::from_string("{}".to_string())
                                    .unwrap()
                            );
                            C::from_parts(&event_type, json).map_err(A::Error::custom)?
                        },
                    };
                }
            } else if is_generic {
                quote! {
                    let json = content.ok_or_else(|| #import_path::exports::serde::de::Error::missing_field("content"))?;
                    let content = C::from_parts(&event_type, json).map_err(A::Error::custom)?;
                }
            } else {
                quote! {
                    let content = content.ok_or_else(
                        || #import_path::exports::serde::de::Error::missing_field("content"),
                    )?;
                }
            }
        } else if name == "prev_content" {
            if is_generic {
                quote! {
                    let prev_content = if let Some(json) = prev_content {
                        Some(C::from_parts(&event_type, json).map_err(A::Error::custom)?)
                    } else {
                        None
                    };
                }
            } else {
                quote! {
                    let prev_content = if let Some(content) = prev_content {
                        Some(content)
                    } else {
                        None
                    };
                }
            }
        } else if name == "origin_server_ts" {
            quote! {
                let origin_server_ts = origin_server_ts
                    .map(|time| {
                        let t = time.into();
                        ::std::time::UNIX_EPOCH + ::std::time::Duration::from_millis(t)
                    })
                    .ok_or_else(|| #import_path::exports::serde::de::Error::missing_field("origin_server_ts"))?;
            }
        } else if name == "unsigned" {
            quote! { let unsigned = unsigned.unwrap_or_default(); }
        } else {
            quote! {
                let #name = #name.ok_or_else(|| {
                    #import_path::exports::serde::de::Error::missing_field(stringify!(#name))
                })?;
            }
        }
    })
    .collect::<Vec<_>>();

    let field_names = fields.iter().flat_map(|f| &f.ident).collect::<Vec<_>>();

    let deserialize_impl_gen = if is_generic {
        let gen = &input.generics.params;
        quote! { <'de, #gen> }
    } else {
        quote! { <'de> }
    };
    let deserialize_phantom_type = if is_generic {
        quote! { ::std::marker::PhantomData }
    } else {
        quote! {}
    };

    Ok(quote! {
        impl #deserialize_impl_gen #import_path::exports::serde::de::Deserialize<'de> for #ident #ty_gen #where_clause {
            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
            where
                D: #import_path::exports::serde::de::Deserializer<'de>,
            {
                #[derive(#import_path::exports::serde::Deserialize)]
                #[serde(field_identifier, rename_all = "snake_case")]
                enum Field {
                    // since this is represented as an enum we have to add it so the JSON picks it
                    // up
                    Type,
                    #( #enum_variants, )*
                    #[serde(other)]
                    Unknown,
                }

                /// Visits the fields of an event struct to handle deserialization of
                /// the `content` and `prev_content` fields.
                struct EventVisitor #impl_generics (#deserialize_phantom_type #ty_gen);

                impl #deserialize_impl_gen #import_path::exports::serde::de::Visitor<'de>
                    for EventVisitor #ty_gen #where_clause
                {
                    type Value = #ident #ty_gen;

                    fn expecting(
                        &self,
                        formatter: &mut ::std::fmt::Formatter<'_>,
                    ) -> ::std::fmt::Result {
                        write!(formatter, "struct implementing {}", stringify!(#content_type))
                    }

                    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
                    where
                        A: #import_path::exports::serde::de::MapAccess<'de>,
                    {
                        use #import_path::exports::serde::de::Error as _;

                        let mut event_type: Option<String> = None;
                        #( let mut #field_names: Option<#deserialize_var_types> = None; )*

                        while let Some(key) = map.next_key()? {
                            match key {
                                Field::Unknown => {
                                    let _: #import_path::exports::serde::de::IgnoredAny = map.next_value()?;
                                },
                                Field::Type => {
                                    if event_type.is_some() {
                                        return Err(#import_path::exports::serde::de::Error::duplicate_field("type"));
                                    }
                                    event_type = Some(map.next_value()?);
                                }
                                #(
                                    Field::#enum_variants => {
                                        if #field_names.is_some() {
                                            return Err(#import_path::exports::serde::de::Error::duplicate_field(
                                                stringify!(#field_names),
                                            ));
                                        }
                                        #field_names = Some(map.next_value()?);
                                    }
                                )*
                            }
                        }

                        let event_type =
                            event_type.ok_or_else(|| #import_path::exports::serde::de::Error::missing_field("type"))?;
                        #( #ok_or_else_fields )*

                        Ok(#ident {
                            #( #field_names ),*
                        })
                    }
                }

                deserializer.deserialize_map(EventVisitor(#deserialize_phantom_type))
            }
        }
    })
}

fn expand_from_into(
    input: &DeriveInput,
    kind: &EventKind,
    var: &EventKindVariation,
    fields: &[Field],
    import_path: &TokenStream,
) -> Option<TokenStream> {
    let ident = &input.ident;

    let (impl_generics, ty_gen, where_clause) = input.generics.split_for_impl();

    let fields = fields.iter().flat_map(|f| &f.ident).collect::<Vec<_>>();

    let fields_without_unsigned =
        fields.iter().filter(|id| id.to_string().as_str() != "unsigned").collect::<Vec<_>>();

    let (into, into_full_event) = if var.is_redacted() {
        (quote! { unsigned: unsigned.into(), }, quote! { unsigned: unsigned.into_full(room_id), })
    } else if kind == &EventKind::Ephemeral {
        (TokenStream::new(), TokenStream::new())
    } else {
        (quote! { unsigned, }, quote! { unsigned, })
    };

    if let EventKindVariation::Sync | EventKindVariation::RedactedSync = var {
        let full_struct = kind.to_event_ident(&var.to_full_variation());
        Some(quote! {
            impl #impl_generics From<#full_struct #ty_gen> for #ident #ty_gen #where_clause {
                fn from(event: #full_struct #ty_gen) -> Self {
                    let #full_struct {
                        #( #fields, )* ..
                    } = event;
                    Self { #( #fields_without_unsigned, )* #into }
                }
            }

            impl #impl_generics #ident #ty_gen #where_clause {
                /// Convert this sync event into a full event, one with a room_id field.
                pub fn into_full_event(
                    self,
                    room_id: #import_path::exports::ruma_identifiers::RoomId,
                ) -> #full_struct #ty_gen {
                    let Self { #( #fields, )* } = self;
                    #full_struct {
                        #( #fields_without_unsigned, )*
                        room_id: room_id.clone(),
                        #into_full_event
                    }
                }
            }
        })
    } else {
        None
    }
}

fn expand_eq_ord_event(input: &DeriveInput, fields: &[Field]) -> Option<TokenStream> {
    if fields.iter().flat_map(|f| f.ident.as_ref()).any(|f| f == "event_id") {
        let ident = &input.ident;
        let (impl_gen, ty_gen, where_clause) = input.generics.split_for_impl();

        Some(quote! {
            impl #impl_gen ::std::cmp::PartialEq for #ident #ty_gen #where_clause {
                /// This checks if two `EventId`s are equal.
                fn eq(&self, other: &Self) -> ::std::primitive::bool {
                    self.event_id == other.event_id
                }
            }

            impl #impl_gen ::std::cmp::Eq for #ident #ty_gen #where_clause {}

            impl #impl_gen ::std::cmp::PartialOrd for #ident #ty_gen #where_clause {
                /// This compares `EventId`s and orders them lexicographically.
                fn partial_cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
                    self.event_id.partial_cmp(&other.event_id)
                }
            }

            impl #impl_gen ::std::cmp::Ord for #ident #ty_gen #where_clause {
                /// This compares `EventId`s and orders them lexicographically.
                fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
                    self.event_id.cmp(&other.event_id)
                }
            }
        })
    } else {
        None
    }
}

/// CamelCase's a field ident like "foo_bar" to "FooBar".
fn to_camel_case(name: &Ident) -> Ident {
    let span = name.span();
    let name = name.to_string();

    let s = name
        .split('_')
        .map(|s| s.chars().next().unwrap().to_uppercase().to_string() + &s[1..])
        .collect::<String>();
    Ident::new(&s, span)
}
<|endoftext|>


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

//! Utilities for detecting and working with indentation.

extern crate xi_rope;

use std::collections::BTreeMap;
use xi_rope::Rope;

/// An enumeration of legal indentation types.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Indentation {
    Tabs,
    Spaces(usize),
}

/// A struct representing the mixed indentation error.
#[derive(Debug)]
pub struct MixedIndentError;

impl Indentation {
    /// Parses a rope for indentation settings.
    pub fn parse(rope: &Rope) -> Result<Option<Self>, MixedIndentError> {
        let lines = rope.lines_raw(..);
        let mut tabs = false;
        let mut spaces: BTreeMap<usize, usize> = BTreeMap::new();

        for line in lines {
            match Indentation::parse_line(&line) {
                Ok(Some(Indentation::Spaces(size))) => {
                    let counter = spaces.entry(size).or_insert(0);
                    *counter += 1;
                }
                Ok(Some(Indentation::Tabs)) => tabs = true,
                Ok(None) => continue,
                Err(e) => return Err(e),
            }
        }

        match (tabs, !spaces.is_empty()) {
            (true, true) => Err(MixedIndentError),
            (true, false) => Ok(Some(Indentation::Tabs)),
            (false, true) => {
                let tab_size = extract_count(spaces);
                if tab_size > 0 {
                    Ok(Some(Indentation::Spaces(tab_size)))
                } else {
                    Ok(None)
                }
            },
            _ => Ok(None),
        }
    }

    /// Detects the indentation on a specific line.
    /// Parses whitespace until first occurrence of something else
    pub fn parse_line(line: &str) -> Result<Option<Self>, MixedIndentError> {
        let mut spaces = 0;

        for char in line.as_bytes() {
            match char {
                b' ' => spaces += 1,
                b'\t' if spaces > 0 => return Err(MixedIndentError),
                b'\t' => return Ok(Some(Indentation::Tabs)),
                _ => break,
            }
        }

        if spaces > 0 {
            Ok(Some(Indentation::Spaces(spaces)))
        } else {
            Ok(None)
        }
    }
}

/// Uses a heuristic to calculate the greatest common denominator of most used indentation depths.
///
/// As BTreeMaps are ordered by value, using take on the iterator ensures the indentation levels
/// most frequently used in the file are extracted.
fn extract_count(spaces: BTreeMap<usize, usize>) -> usize {
    let mut take_size = 4;

    if spaces.len() < take_size {
        take_size = spaces.len();
    }

    // Fold results using GCD, skipping numbers which result in gcd returning 1
    spaces.iter().take(take_size).fold(0, |a, (b, _)| {
        let d = gcd(a, *b);
        if d == 1 {
            a
        } else {
            d
        }
    })
}

/// Simple implementation to calculate greatest common divisor, based on Euclid's algorithm
fn gcd(a: usize, b: usize) -> usize {
    if a == 0 {
        b
    } else if b == 0 || a == b {
        a
    } else {
        let mut a = a;
        let mut b = b;

        while b > 0 {
            let r = a % b;
            a = b;
            b = r;
        }
        a
    }
}

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

    #[test]
    fn gcd_calculates_correctly() {
        assert_eq!(21, gcd(1071, 462));
        assert_eq!(6, gcd(270, 192));
    }

    #[test]
    fn line_gets_two_spaces() {
        let result = Indentation::parse_line("  ");
        let expected = Indentation::Spaces(2);

        assert_eq!(result.unwrap(), Some(expected));
    }

    #[test]
    fn line_gets_tabs() {
        let result = Indentation::parse_line("\t");
        let expected = Indentation::Tabs;

        assert_eq!(result.unwrap(), Some(expected));
    }

    #[test]
    fn line_errors_mixed_indent() {
        let result = Indentation::parse_line("  \t");
        assert!(result.is_err());
    }

    #[test]
    fn rope_gets_two_spaces() {
        let result = Indentation::parse(&Rope::from(
            r#"
        // This is a comment
          Testing
          Indented
            Even more indented
            # Comment
            # Comment
            # Comment
        "#,
        ));
        let expected = Indentation::Spaces(2);

        assert_eq!(result.unwrap(), Some(expected));
    }

    #[test]
    fn rope_gets_four_spaces() {
        let result = Indentation::parse(&Rope::from(
            r#"
        fn my_fun_func(&self,
                       another_arg: usize) -> Fun {
            /* Random comment describing program behavior */
            Fun::from(another_arg)
        }
        "#,
        ));
        let expected = Indentation::Spaces(4);

        assert_eq!(result.unwrap(), Some(expected));
    }
}
<commit_msg>Add regression test for whitespace detection<commit_after>// Copyright 2018 The xi-editor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Utilities for detecting and working with indentation.

extern crate xi_rope;

use std::collections::BTreeMap;
use xi_rope::Rope;

/// An enumeration of legal indentation types.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Indentation {
    Tabs,
    Spaces(usize),
}

/// A struct representing the mixed indentation error.
#[derive(Debug)]
pub struct MixedIndentError;

impl Indentation {
    /// Parses a rope for indentation settings.
    pub fn parse(rope: &Rope) -> Result<Option<Self>, MixedIndentError> {
        let lines = rope.lines_raw(..);
        let mut tabs = false;
        let mut spaces: BTreeMap<usize, usize> = BTreeMap::new();

        for line in lines {
            match Indentation::parse_line(&line) {
                Ok(Some(Indentation::Spaces(size))) => {
                    let counter = spaces.entry(size).or_insert(0);
                    *counter += 1;
                }
                Ok(Some(Indentation::Tabs)) => tabs = true,
                Ok(None) => continue,
                Err(e) => return Err(e),
            }
        }

        match (tabs, !spaces.is_empty()) {
            (true, true) => Err(MixedIndentError),
            (true, false) => Ok(Some(Indentation::Tabs)),
            (false, true) => {
                let tab_size = extract_count(spaces);
                if tab_size > 0 {
                    Ok(Some(Indentation::Spaces(tab_size)))
                } else {
                    Ok(None)
                }
            }
            _ => Ok(None),
        }
    }

    /// Detects the indentation on a specific line.
    /// Parses whitespace until first occurrence of something else
    pub fn parse_line(line: &str) -> Result<Option<Self>, MixedIndentError> {
        let mut spaces = 0;

        for char in line.as_bytes() {
            match char {
                b' ' => spaces += 1,
                b'\t' if spaces > 0 => return Err(MixedIndentError),
                b'\t' => return Ok(Some(Indentation::Tabs)),
                _ => break,
            }
        }

        if spaces > 0 {
            Ok(Some(Indentation::Spaces(spaces)))
        } else {
            Ok(None)
        }
    }
}

/// Uses a heuristic to calculate the greatest common denominator of most used indentation depths.
///
/// As BTreeMaps are ordered by value, using take on the iterator ensures the indentation levels
/// most frequently used in the file are extracted.
fn extract_count(spaces: BTreeMap<usize, usize>) -> usize {
    let mut take_size = 4;

    if spaces.len() < take_size {
        take_size = spaces.len();
    }

    // Fold results using GCD, skipping numbers which result in gcd returning 1
    spaces.iter().take(take_size).fold(0, |a, (b, _)| {
        let d = gcd(a, *b);
        if d == 1 {
            a
        } else {
            d
        }
    })
}

/// Simple implementation to calculate greatest common divisor, based on Euclid's algorithm
fn gcd(a: usize, b: usize) -> usize {
    if a == 0 {
        b
    } else if b == 0 || a == b {
        a
    } else {
        let mut a = a;
        let mut b = b;

        while b > 0 {
            let r = a % b;
            a = b;
            b = r;
        }
        a
    }
}

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

    #[test]
    fn gcd_calculates_correctly() {
        assert_eq!(21, gcd(1071, 462));
        assert_eq!(6, gcd(270, 192));
    }

    #[test]
    fn line_gets_two_spaces() {
        let result = Indentation::parse_line("  ");
        let expected = Indentation::Spaces(2);

        assert_eq!(result.unwrap(), Some(expected));
    }

    #[test]
    fn line_gets_tabs() {
        let result = Indentation::parse_line("\t");
        let expected = Indentation::Tabs;

        assert_eq!(result.unwrap(), Some(expected));
    }

    #[test]
    fn line_errors_mixed_indent() {
        let result = Indentation::parse_line("  \t");
        assert!(result.is_err());
    }

    #[test]
    fn rope_gets_two_spaces() {
        let result = Indentation::parse(&Rope::from(
            r#"
        // This is a comment
          Testing
          Indented
            Even more indented
            # Comment
            # Comment
            # Comment
        "#,
        ));
        let expected = Indentation::Spaces(2);

        assert_eq!(result.unwrap(), Some(expected));
    }

    #[test]
    fn rope_gets_four_spaces() {
        let result = Indentation::parse(&Rope::from(
            r#"
        fn my_fun_func(&self,
                       another_arg: usize) -> Fun {
            /* Random comment describing program behavior */
            Fun::from(another_arg)
        }
        "#,
        ));
        let expected = Indentation::Spaces(4);

        assert_eq!(result.unwrap(), Some(expected));
    }

    #[test]
    fn rope_returns_none() {
        let result = Indentation::parse(&Rope::from(
            r#"# Readme example
 1. One space.
But the majority is still 0.
"#,
        ));

        assert_eq!(result.unwrap(), None);
    }
}
<|endoftext|>


<commit_before>
use std::cmp;

fn is_blk(size: (usize, usize)) -> bool {
	return size.0 % 4 == 0 && size.1 % 4 == 0;
}

fn bc1_encode_endpoint(pix: &[i16; 4]) -> i16 {
	((pix[0] >> 3) << 11) |
	((pix[1] >> 2) << 5) |
	(pix[2] >> 3)
}

fn bc1_interp(min: &[i16; 4], max: &[i16; 4], index: usize) -> [u8; 4] {
	let coefs = match index {
		0x0 => (3, 0),
		0x1 => (0, 3),
		0x2 => (2 ,1),
		_ => (1, 2)
	};
	[((coefs.1 * min[0] + coefs.0 * max[0]) / 3) as u8,
	 ((coefs.1 * min[1] + coefs.0 * max[1]) / 3) as u8,
	 ((coefs.1 * min[2] + coefs.0 * max[2]) / 3) as u8,
	 ((coefs.1 * min[3] + coefs.0 * max[3]) / 3) as u8]
}

fn bc1_table(min: &[i16; 4], max: &[i16; 4]) -> [[u8; 4]; 4] {
	let mut interps = [[0u8; 4]; 4];
	for i in 0..4 {
		interps[i] = bc1_interp(&min, &max, i);
	}
	interps
}

fn bc1_minmax(pixels: &[[u8; 4]; 16]) -> ([i16; 4], [i16; 4]) {
	let mut min = [255i16; 4];
	let mut max = [0i16; 4];
	for p in pixels.into_iter() {
		for i in 0..4 {
			min[i] = cmp::min(min[i], p[i] as i16);
			max[i] = cmp::max(max[i], p[i] as i16);
		}
	}
	(min, max)
}

fn bc1_dist_one(a: i16, b: i16) -> u32 {
	(a - b).abs() as u32
}

fn bc1_dist(a: &[u8; 4], b: &[u8; 4]) -> u32 {
	bc1_dist_one(a[0] as i16, b[0] as i16) +
	bc1_dist_one(a[1] as i16, b[1] as i16) +
	bc1_dist_one(a[2] as i16, b[2] as i16) +
	bc1_dist_one(a[3] as i16, b[3] as i16)
}

fn bc1_encode(table: &[[u8; 4]; 4], pixels: &[[u8; 4]; 16]) -> u32 {
	let mut mask = 0u32;
	for pix in pixels.into_iter().rev() {
		let best = (0..4).into_iter().min_by_key(|x| bc1_dist(&table[*x], pix)).unwrap() as u32;
		mask = (mask << 2) | (best & 0x03);
	}
	mask
}

fn bc1_total_dist(table: &[[u8; 4]; 4], pixels: &[[u8; 4]; 16]) -> u32 {
	let mut sum = 0u32;
	for pix in pixels.into_iter().rev() {
		sum += (0..4).into_iter().map(|x| bc1_dist(&table[x], pix)).min().unwrap();
	}
	sum
}

fn iter_endpoint(mut to_iter: [i16; 4], other: &[i16; 4], pixels: &[[u8; 4]; 16], offsets: &[i16]) -> [i16; 4] {
	let (mut best, mut index, mut offset) = (bc1_total_dist(&bc1_table(&to_iter, other), pixels), 0, 0);
	for i in 0..3 {
		for o in offsets.into_iter() {
			let mut x = to_iter;
			x[i] += *o;
			let table = bc1_table(&x, &other);
			let dist = bc1_total_dist(&table, pixels);
			if dist < best {
				best = dist;
				index = i;
				offset = *o;
			}
		}
	}
	to_iter[index] += offset;
	to_iter
}

pub fn bc1(image: &Vec<u8>, size: (usize, usize), quality: u8) -> Result<Vec<u8>, ()> {
	if !is_blk(size) {
		return Err(());
	}

    let mut out = Vec::with_capacity(image.len() / 8);

   	//let mut total_distance = 0u64;

    let block_size = 4;
    let blk_count = (size.0 / block_size, size.1 / block_size);

   	for x in 0..blk_count.0 {
   		for y in 0..blk_count.1 {

   			let mut pixels = [[0u8; 4]; 16]; // No compile time constants
   			for px in 0..block_size {
   				for py in 0..block_size {
   					let x = x * block_size + px;
   					let y = y * block_size + py;

   					let pi = (x * size.0 + y) * 4; // in bytes
   					let pix = [image[pi], image[pi + 1], image[pi + 2], image[pi + 3]];

   					pixels[px * block_size + py] = pix;
   				}
   			}

   			let (mut min, mut max) = bc1_minmax(&pixels);

   			for _ in 0..quality {
   				min = iter_endpoint(min, &max, &pixels, &[2, 4, 8]);
   				max = iter_endpoint(max, &min, &pixels, &[-2, -4, -8]);
   			}

   			let table = bc1_table(&min, &max);

   			//total_distance += bc1_total_dist(&table, &pixels) as u64;

   			// if we want alpha swap min/max (or encode min first)
			let min = bc1_encode_endpoint(&min);
			let max = bc1_encode_endpoint(&max);

			out.push((max & 0xFF) as u8);
			out.push((max >> 8) as u8);

			out.push((min & 0xFF) as u8);
			out.push((min >> 8) as u8);

			let mut encoded = bc1_encode(&table, &pixels);
			for _ in 0..4 {
				out.push((encoded & 0xFF) as u8);
				encoded >>= 8;
			}
		}
	}
	//println!("total = {}\nper-pixel = {}", total_distance, total_distance as f64/(size.0 * size.1) as f64);
	Ok(out)
}
<commit_msg>More texture encoder work<commit_after>
use std::cmp;

type Endpoint = [u16; 4];
type Rgba = [u8; 4];

fn is_blk(size: (usize, usize)) -> bool {
	return size.0 % 4 == 0 && size.1 % 4 == 0;
}

fn bc1_encode_endpoint(pix: &Endpoint) -> u16 {
	((pix[0] >> 3) << 11) |
	((pix[1] >> 2) << 5) |
	(pix[2] >> 3)
}

fn bc1_build_entry(min: &Endpoint, max: &Endpoint, index: usize) -> Rgba {
	let coefs = match index {
		0x0 => (3, 0),
		0x1 => (0, 3),
		0x2 => (2 ,1),
		_ => (1, 2)
	};
	[((coefs.1 * min[0] + coefs.0 * max[0]) / 3) as u8,
	 ((coefs.1 * min[1] + coefs.0 * max[1]) / 3) as u8,
	 ((coefs.1 * min[2] + coefs.0 * max[2]) / 3) as u8,
	 ((coefs.1 * min[3] + coefs.0 * max[3]) / 3) as u8]
}

fn bc1_build_table(min: &Endpoint, max: &Endpoint) -> [Rgba; 4] {
	let mut interps = [[0u8; 4]; 4];
	for i in 0..4 {
		interps[i] = bc1_build_entry(&min, &max, i);
	}
	interps
}

fn bc1_minmax(pixels: &[Rgba; 16]) -> (Endpoint, Endpoint) {
	let mut min = [256u16; 4];
	let mut max = [0u16; 4];
	for p in pixels.into_iter() {
		for i in 0..4 {
			min[i] = cmp::min(min[i], p[i] as u16);
			max[i] = cmp::max(max[i], p[i] as u16);
		}
	}
	(min, max)
}

fn bc1_dist_one(a: i16, b: i16) -> u32 {
	(a - b).abs() as u32
}

fn bc1_dist(a: &Rgba, b: &Rgba) -> u32 {
	bc1_dist_one(a[0] as i16, b[0] as i16) +
	bc1_dist_one(a[1] as i16, b[1] as i16) +
	bc1_dist_one(a[2] as i16, b[2] as i16) +
	bc1_dist_one(a[3] as i16, b[3] as i16)
}

fn bc1_encode_pixel(table: &[Rgba; 4], pixels: &[Rgba; 16]) -> u32 {
	let mut mask = 0u32;
	for pix in pixels.into_iter().rev() {
		let best = (0..4).into_iter().min_by_key(|x| bc1_dist(&table[*x], pix)).unwrap() as u32;
		mask = (mask << 2) | (best & 0x03);
	}
	mask
}

/*fn bc1_total_dist(table: &[Rgba; 4], pixels: &[Rgba; 16]) -> u32 {
	let mut sum = 0u32;
	for pix in pixels.into_iter().rev() {
		sum += (0..4).into_iter().map(|x| bc1_dist(&table[x], pix)).min().unwrap();
	}
	sum
}

fn iter_endpoint(mut to_iter: Endpoint, other: &Endpoint, pixels: &[Rgba; 16], offsets: &[i16]) -> [u16; 4] {
	let (mut best, mut index, mut offset) = (bc1_total_dist(&bc1_build_table(&to_iter, other), pixels), 0, 0);
	for i in 0..3 {
		for o in offsets.into_iter() {
			let mut x = to_iter;
			x[i] += *o;
			let table = bc1_build_table(&x, &other);
			let dist = bc1_total_dist(&table, pixels);
			if dist < best {
				best = dist;
				index = i;
				offset = *o;
			}
		}
	}
	to_iter[index] += offset;
	to_iter
}*/

fn bc1_encode_block(pixels: &[Rgba; 16]) -> u64 {
	let (min, max) = bc1_minmax(&pixels);

	/*for _ in 0..quality {
		min = iter_endpoint(min, &max, &pixels, &[2, 4, 8]);
		max = iter_endpoint(max, &min, &pixels, &[-2, -4, -8]);
	}*/

	let table = bc1_build_table(&min, &max);

	let min = bc1_encode_endpoint(&min) as u64;
	let max = bc1_encode_endpoint(&max) as u64;

	let encoded = bc1_encode_pixel(&table, &pixels) as u64;

	(encoded << 32) | (min << 16) | max
}

pub fn bc1(image: &Vec<u8>, size: (usize, usize), _: u8) -> Result<Vec<u8>, ()> {
	if !is_blk(size) {
		return Err(());
	}

    let mut out = Vec::with_capacity(image.len() / 8);

    let block_size = 4;
    let blk_count = (size.0 / block_size, size.1 / block_size);

   	for x in 0..blk_count.0 {
   		for y in 0..blk_count.1 {

   			let mut pixels = [[0u8; 4]; 16]; // No compile time constants
   			for px in 0..block_size {
   				for py in 0..block_size {
   					let x = x * block_size + px;
   					let y = y * block_size + py;

   					let pi = (x * size.0 + y) * 4; // in bytes
   					let pix = [image[pi], image[pi + 1], image[pi + 2], image[pi + 3]];

   					pixels[px * block_size + py] = pix;
   				}
   			}

   			let mut encoded = bc1_encode_block(&pixels);

   			for _ in 0..8 {
   				out.push((encoded & 0xFF) as u8);
   				encoded >>= 8;
   			}
		}
	}
	Ok(out)
}
<|endoftext|>