<commit_before>use super::gi_docgen;
use crate::{nameutil, Env};
use log::{info, warn};
use once_cell::sync::Lazy;
use regex::{Captures, Regex};

const LANGUAGE_SEP_BEGIN: &str = "<!-- language=\"";
const LANGUAGE_SEP_END: &str = "\" -->";
const LANGUAGE_BLOCK_BEGIN: &str = "|[";
const LANGUAGE_BLOCK_END: &str = "\n]|";

pub fn reformat_doc(input: &str, env: &Env, in_type: &str) -> String {
    code_blocks_transformation(input, env, in_type)
}

fn try_split<'a>(src: &'a str, needle: &str) -> (&'a str, Option<&'a str>) {
    match src.find(needle) {
        Some(pos) => (&src[..pos], Some(&src[pos + needle.len()..])),
        None => (src, None),
    }
}

fn code_blocks_transformation(mut input: &str, env: &Env, in_type: &str) -> String {
    let mut out = String::with_capacity(input.len());

    loop {
        input = match try_split(input, LANGUAGE_BLOCK_BEGIN) {
            (before, Some(after)) => {
                out.push_str(&format(before, env, in_type));
                if let (before, Some(after)) =
                    try_split(get_language(after, &mut out), LANGUAGE_BLOCK_END)
                {
                    out.push_str(before);
                    out.push_str("\n```");
                    after
                } else {
                    after
                }
            }
            (before, None) => {
                out.push_str(&format(before, env, in_type));
                return out;
            }
        };
    }
}

fn get_language<'a>(entry: &'a str, out: &mut String) -> &'a str {
    if let (_, Some(after)) = try_split(entry, LANGUAGE_SEP_BEGIN) {
        if let (before, Some(after)) = try_split(after, LANGUAGE_SEP_END) {
            out.push_str(&format!("\n```{}", before));
            return after;
        }
    }
    out.push_str("\n```text");
    entry
}

fn format(input: &str, env: &Env, in_type: &str) -> String {
    let mut ret = String::with_capacity(input.len());
    // We run gi_docgen first because it's super picky about the types it replaces
    let out = replace_c_types(input, env, in_type);
    let out = gi_docgen::replace_c_types(&out, env, in_type);
    // this has to be done after gi_docgen replaced the various types it knows as it uses `@` in it's linking format
    let out = PARAM_SYMBOL.replace_all(&out, |caps: &Captures<'_>| format!("`{}`", &caps[2]));
    ret.push_str(&out);
    ret
}

static SYMBOL: Lazy<Regex> = Lazy::new(|| Regex::new(r"([#%])(\w+\b)([:.]+[\w-]+\b)?").unwrap());
static PARAM_SYMBOL: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"([@])(\w+\b)([:.]+[\w-]+\b)?").unwrap());
static FUNCTION: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"([@#%])?(\w+\b[:.]+)?(\b[a-z0-9_]+)\(\)").unwrap());
// **note**
// The optional . at the end is to make the regex more relaxed for some weird broken cases on gtk3's docs
// it doesn't hurt other docs so please don't drop it
static GDK_GTK: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"`([^\(:])?((G[dts]k|Pango)\w+\b)(\.)?`").unwrap());
static TAGS: Lazy<Regex> = Lazy::new(|| Regex::new(r"<[\w/-]+>").unwrap());
static SPACES: Lazy<Regex> = Lazy::new(|| Regex::new(r"[ ]{2,}").unwrap());

fn replace_c_types(entry: &str, env: &Env, _in_type: &str) -> String {
    let out = FUNCTION.replace_all(entry, |caps: &Captures<'_>| {
        let name = &caps[3];
        find_function(name, env).unwrap_or_else(|| {
            info!("Function not found, falling back to symbol name `{}`", name);
            format!("`{}`", name)
        })
    });

    let out = SYMBOL.replace_all(&out, |caps: &Captures<'_>| {
        match &caps[2] {
            "TRUE" => "[`true`]".to_string(),
            "FALSE" => "[`false`]".to_string(),
            "NULL" => "[`None`]".to_string(),
            symbol_name => {
                if &caps[1] == "%" {
                    find_constant_or_variant(symbol_name, env)
                } else {
                    let method_name = caps.get(3).map(|m| m.as_str().trim_start_matches('.'));
                    // would be #
                    find_method_or_type(symbol_name, method_name, env)
                }
                .unwrap_or_else(|| {
                    info!("Symbol not found: `{}`", symbol_name);

                    format!("`{}`", symbol_name)
                })
            }
        }
    });
    let out = GDK_GTK.replace_all(&out, |caps: &Captures<'_>| {
        find_type(&caps[2], env).unwrap_or_else(|| format!("`{}`", &caps[2]))
    });
    let out = TAGS.replace_all(&out, "`$0`");
    SPACES.replace_all(&out, " ").into_owned()
}

fn find_method_or_type(type_: &str, method_name: Option<&str>, env: &Env) -> Option<String> {
    let symbols = env.symbols.borrow();
    if let Some(method) = method_name {
        if let Some((obj_info, fn_info)) = env.analysis.find_object_by_function(
            env,
            |o| o.full_name == type_,
            |f| f.name == method,
        ) {
            let sym = symbols.by_tid(obj_info.type_id).unwrap(); // we are sure the object exists
            let (type_name, visible_type_name) = obj_info.generate_doc_link_info(fn_info);
            let name = sym.full_rust_name().replace(&obj_info.name, &type_name);

            Some(fn_info.doc_link(Some(&name), Some(&visible_type_name)))
        } else if let Some((record_info, fn_info)) = env.analysis.find_record_by_function(
            env,
            |r| r.type_(&env.library).c_type == type_,
            |f| f.name == method,
        ) {
            let sym_name = symbols
                .by_tid(record_info.type_id)
                .unwrap()
                .full_rust_name(); // we are sure the object exists
            Some(fn_info.doc_link(Some(&sym_name), None))
        } else {
            warn!("Method `{}` of type `{}` was not found", method, type_);
            None
        }
    } else {
        find_type(type_, env)
    }
}

fn find_constant_or_variant(symbol: &str, env: &Env) -> Option<String> {
    let symbols = env.symbols.borrow();
    if let Some((flag_info, member_info)) = env.analysis.flags.iter().find_map(|f| {
        f.type_(&env.library)
            .members
            .iter()
            .find(|m| m.c_identifier == symbol && !m.status.ignored())
            .map(|m| (f, m))
    }) {
        let sym = symbols.by_tid(flag_info.type_id).unwrap();
        Some(format!(
            "[`{p}::{m}`][crate::{p}::{m}]",
            m = nameutil::bitfield_member_name(&member_info.name),
            p = sym.full_rust_name()
        ))
    } else if let Some((enum_info, member_info)) = env.analysis.enumerations.iter().find_map(|e| {
        e.type_(&env.library)
            .members
            .iter()
            .find(|m| m.c_identifier == symbol && !m.status.ignored())
            .map(|m| (e, m))
    }) {
        let sym = symbols.by_tid(enum_info.type_id).unwrap();
        Some(format!(
            "[`{e}::{m}`][crate::{e}::{m}]",
            m = nameutil::enum_member_name(&member_info.name),
            e = sym.full_rust_name()
        ))
    } else if let Some(const_info) = env
        .analysis
        .constants
        .iter()
        .find(|c| c.glib_name == symbol)
    {
        // for whatever reason constants are not part of the symbols list
        Some(format!("[`{n}`][crate::{n}]", n = const_info.name))
    } else {
        warn!(
            "Constant/Flag variant/Enum member `{}` was not found",
            symbol
        );
        None
    }
}

/// either an object/interface, record, enum or a flag
const IGNORED_C_TYPES: [&str; 6] = [
    "gconstpointer",
    "guint16",
    "guint",
    "gunicode",
    "gchararray",
    "GList",
];
fn find_type(type_: &str, env: &Env) -> Option<String> {
    if IGNORED_C_TYPES.contains(&type_) {
        return None;
    }

    let symbols = env.symbols.borrow();

    let symbol = if let Some(obj) = env.analysis.objects.values().find(|o| o.c_type == type_) {
        symbols.by_tid(obj.type_id)
    } else if let Some(record) = env
        .analysis
        .records
        .values()
        .find(|r| r.type_(&env.library).c_type == type_)
    {
        symbols.by_tid(record.type_id)
    } else if let Some(enum_) = env
        .analysis
        .enumerations
        .iter()
        .find(|e| e.type_(&env.library).c_type == type_)
    {
        symbols.by_tid(enum_.type_id)
    } else if let Some(flag) = env
        .analysis
        .flags
        .iter()
        .find(|f| f.type_(&env.library).c_type == type_)
    {
        symbols.by_tid(flag.type_id)
    } else {
        warn!("Object/Interface/Record not found: `{}`", type_);
        None
    };

    symbol.map_or_else(
        || {
            find_constant_or_variant(type_, env).map(|i| {
                warn!(
                    "`{}` should be a type (`#`) but was parsed as constant or variant (`%`)",
                    type_
                );
                i
            })
        },
        |sym| Some(format!("[`{n}`][crate::{n}]", n = sym.full_rust_name())),
    )
}

/// Find a function in all the possible items, if not found return the original name surrounded with backsticks
/// A function can either be a struct/interface/record method, a global function or maybe a virtual function

const IGNORE_C_WARNING_FUNCS: [&str; 6] = [
    "g_object_unref",
    "g_object_ref",
    "g_free",
    "g_list_free",
    "g_strfreev",
    "printf",
];
fn find_function(name: &str, env: &Env) -> Option<String> {
    let symbols = env.symbols.borrow();
    // if we can find the function in an object
    if let Some((obj_info, fn_info)) =
        env.analysis
            .find_object_by_function(env, |_| true, |f| f.glib_name == name)
    {
        let sym = symbols.by_tid(obj_info.type_id).unwrap(); // we are sure the object exists
        let (type_name, visible_type_name) = obj_info.generate_doc_link_info(fn_info);

        let name = sym.full_rust_name().replace(&obj_info.name, &type_name);
        Some(fn_info.doc_link(Some(&name), Some(&visible_type_name)))
    // or in a record
    } else if let Some((record_info, fn_info)) =
        env.analysis
            .find_record_by_function(env, |_| true, |f| f.glib_name == name)
    {
        let sym_name = symbols
            .by_tid(record_info.type_id)
            .unwrap()
            .full_rust_name(); // we are sure the object exists
        Some(fn_info.doc_link(Some(&sym_name), None))
    // or as a global function
    } else if let Some(fn_info) = env
        .analysis
        .find_global_function(env, |f| f.glib_name == name)
    {
        Some(fn_info.doc_link(None, None))
    } else {
        if !IGNORE_C_WARNING_FUNCS.contains(&name) {
            warn!("Function not found found: `{}`", name);
        }
        None
    }
}
<commit_msg>docs: handle properties/signals when looking for a method<commit_after>use super::gi_docgen;
use crate::{nameutil, Env};
use log::{info, warn};
use once_cell::sync::Lazy;
use regex::{Captures, Regex};

const LANGUAGE_SEP_BEGIN: &str = "<!-- language=\"";
const LANGUAGE_SEP_END: &str = "\" -->";
const LANGUAGE_BLOCK_BEGIN: &str = "|[";
const LANGUAGE_BLOCK_END: &str = "\n]|";

pub fn reformat_doc(input: &str, env: &Env, in_type: &str) -> String {
    code_blocks_transformation(input, env, in_type)
}

fn try_split<'a>(src: &'a str, needle: &str) -> (&'a str, Option<&'a str>) {
    match src.find(needle) {
        Some(pos) => (&src[..pos], Some(&src[pos + needle.len()..])),
        None => (src, None),
    }
}

fn code_blocks_transformation(mut input: &str, env: &Env, in_type: &str) -> String {
    let mut out = String::with_capacity(input.len());

    loop {
        input = match try_split(input, LANGUAGE_BLOCK_BEGIN) {
            (before, Some(after)) => {
                out.push_str(&format(before, env, in_type));
                if let (before, Some(after)) =
                    try_split(get_language(after, &mut out), LANGUAGE_BLOCK_END)
                {
                    out.push_str(before);
                    out.push_str("\n```");
                    after
                } else {
                    after
                }
            }
            (before, None) => {
                out.push_str(&format(before, env, in_type));
                return out;
            }
        };
    }
}

fn get_language<'a>(entry: &'a str, out: &mut String) -> &'a str {
    if let (_, Some(after)) = try_split(entry, LANGUAGE_SEP_BEGIN) {
        if let (before, Some(after)) = try_split(after, LANGUAGE_SEP_END) {
            out.push_str(&format!("\n```{}", before));
            return after;
        }
    }
    out.push_str("\n```text");
    entry
}

fn format(input: &str, env: &Env, in_type: &str) -> String {
    let mut ret = String::with_capacity(input.len());
    // We run gi_docgen first because it's super picky about the types it replaces
    let out = replace_c_types(input, env, in_type);
    let out = gi_docgen::replace_c_types(&out, env, in_type);
    // this has to be done after gi_docgen replaced the various types it knows as it uses `@` in it's linking format
    let out = PARAM_SYMBOL.replace_all(&out, |caps: &Captures<'_>| format!("`{}`", &caps[2]));
    ret.push_str(&out);
    ret
}

static SYMBOL: Lazy<Regex> = Lazy::new(|| Regex::new(r"([#%])(\w+\b)([:.]+[\w-]+\b)?").unwrap());
static PARAM_SYMBOL: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"([@])(\w+\b)([:.]+[\w-]+\b)?").unwrap());
static FUNCTION: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"([@#%])?(\w+\b[:.]+)?(\b[a-z0-9_]+)\(\)").unwrap());
// **note**
// The optional . at the end is to make the regex more relaxed for some weird broken cases on gtk3's docs
// it doesn't hurt other docs so please don't drop it
static GDK_GTK: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"`([^\(:])?((G[dts]k|Pango)\w+\b)(\.)?`").unwrap());
static TAGS: Lazy<Regex> = Lazy::new(|| Regex::new(r"<[\w/-]+>").unwrap());
static SPACES: Lazy<Regex> = Lazy::new(|| Regex::new(r"[ ]{2,}").unwrap());

fn replace_c_types(entry: &str, env: &Env, _in_type: &str) -> String {
    let out = FUNCTION.replace_all(entry, |caps: &Captures<'_>| {
        let name = &caps[3];
        find_function(name, env).unwrap_or_else(|| {
            info!("Function not found, falling back to symbol name `{}`", name);
            format!("`{}`", name)
        })
    });

    let out = SYMBOL.replace_all(&out, |caps: &Captures<'_>| {
        match &caps[2] {
            "TRUE" => "[`true`]".to_string(),
            "FALSE" => "[`false`]".to_string(),
            "NULL" => "[`None`]".to_string(),
            symbol_name => {
                if &caps[1] == "%" {
                    find_constant_or_variant(symbol_name, env)
                } else {
                    let method_name = caps.get(3).map(|m| m.as_str().trim_start_matches('.'));
                    // would be #
                    find_method_or_type(symbol_name, method_name, env)
                }
                .unwrap_or_else(|| {
                    info!("Symbol not found: `{}`", symbol_name);

                    format!("`{}`", symbol_name)
                })
            }
        }
    });
    let out = GDK_GTK.replace_all(&out, |caps: &Captures<'_>| {
        find_type(&caps[2], env).unwrap_or_else(|| format!("`{}`", &caps[2]))
    });
    let out = TAGS.replace_all(&out, "`$0`");
    SPACES.replace_all(&out, " ").into_owned()
}

fn find_method_or_type(type_: &str, method_name: Option<&str>, env: &Env) -> Option<String> {
    let symbols = env.symbols.borrow();
    if let Some(method) = method_name {
        let is_signal = method.starts_with("::");
        let is_property = !is_signal && method.starts_with(':');
        if !is_signal && !is_property {
            if let Some((obj_info, fn_info)) = env.analysis.find_object_by_function(
                env,
                |o| o.full_name == type_,
                |f| f.name == method,
            ) {
                let sym = symbols.by_tid(obj_info.type_id).unwrap(); // we are sure the object exists
                let (type_name, visible_type_name) = obj_info.generate_doc_link_info(fn_info);
                let name = sym.full_rust_name().replace(&obj_info.name, &type_name);

                Some(fn_info.doc_link(Some(&name), Some(&visible_type_name)))
            } else if let Some((record_info, fn_info)) = env.analysis.find_record_by_function(
                env,
                |r| r.type_(&env.library).c_type == type_,
                |f| f.name == method,
            ) {
                let sym_name = symbols
                    .by_tid(record_info.type_id)
                    .unwrap()
                    .full_rust_name(); // we are sure the object exists
                Some(fn_info.doc_link(Some(&sym_name), None))
            } else {
                warn!("Method `{}` of type `{}` was not found", method, type_);
                None
            }
        } else {
            env.analysis
                .objects
                .values()
                .find(|o| o.full_name == type_)
                .map(|info| {
                    let sym = symbols.by_tid(info.type_id).unwrap(); // we are sure the object exists
                    let name = method.trim_start_matches(':');
                    if is_property {
                        format!("`property::{}::{}`", sym.full_rust_name(), name)
                    } else {
                        format!("`signal::{}::{}`", sym.full_rust_name(), name)
                    }
                })
        }
    } else {
        find_type(type_, env)
    }
}

fn find_constant_or_variant(symbol: &str, env: &Env) -> Option<String> {
    let symbols = env.symbols.borrow();
    if let Some((flag_info, member_info)) = env.analysis.flags.iter().find_map(|f| {
        f.type_(&env.library)
            .members
            .iter()
            .find(|m| m.c_identifier == symbol && !m.status.ignored())
            .map(|m| (f, m))
    }) {
        let sym = symbols.by_tid(flag_info.type_id).unwrap();
        Some(format!(
            "[`{p}::{m}`][crate::{p}::{m}]",
            m = nameutil::bitfield_member_name(&member_info.name),
            p = sym.full_rust_name()
        ))
    } else if let Some((enum_info, member_info)) = env.analysis.enumerations.iter().find_map(|e| {
        e.type_(&env.library)
            .members
            .iter()
            .find(|m| m.c_identifier == symbol && !m.status.ignored())
            .map(|m| (e, m))
    }) {
        let sym = symbols.by_tid(enum_info.type_id).unwrap();
        Some(format!(
            "[`{e}::{m}`][crate::{e}::{m}]",
            m = nameutil::enum_member_name(&member_info.name),
            e = sym.full_rust_name()
        ))
    } else if let Some(const_info) = env
        .analysis
        .constants
        .iter()
        .find(|c| c.glib_name == symbol)
    {
        // for whatever reason constants are not part of the symbols list
        Some(format!("[`{n}`][crate::{n}]", n = const_info.name))
    } else {
        warn!(
            "Constant/Flag variant/Enum member `{}` was not found",
            symbol
        );
        None
    }
}

/// either an object/interface, record, enum or a flag
const IGNORED_C_TYPES: [&str; 6] = [
    "gconstpointer",
    "guint16",
    "guint",
    "gunicode",
    "gchararray",
    "GList",
];
fn find_type(type_: &str, env: &Env) -> Option<String> {
    if IGNORED_C_TYPES.contains(&type_) {
        return None;
    }

    let symbols = env.symbols.borrow();

    let symbol = if let Some(obj) = env.analysis.objects.values().find(|o| o.c_type == type_) {
        symbols.by_tid(obj.type_id)
    } else if let Some(record) = env
        .analysis
        .records
        .values()
        .find(|r| r.type_(&env.library).c_type == type_)
    {
        symbols.by_tid(record.type_id)
    } else if let Some(enum_) = env
        .analysis
        .enumerations
        .iter()
        .find(|e| e.type_(&env.library).c_type == type_)
    {
        symbols.by_tid(enum_.type_id)
    } else if let Some(flag) = env
        .analysis
        .flags
        .iter()
        .find(|f| f.type_(&env.library).c_type == type_)
    {
        symbols.by_tid(flag.type_id)
    } else {
        warn!("Object/Interface/Record not found: `{}`", type_);
        None
    };

    symbol.map_or_else(
        || {
            find_constant_or_variant(type_, env).map(|i| {
                warn!(
                    "`{}` should be a type (`#`) but was parsed as constant or variant (`%`)",
                    type_
                );
                i
            })
        },
        |sym| Some(format!("[`{n}`][crate::{n}]", n = sym.full_rust_name())),
    )
}

/// Find a function in all the possible items, if not found return the original name surrounded with backsticks
/// A function can either be a struct/interface/record method, a global function or maybe a virtual function

const IGNORE_C_WARNING_FUNCS: [&str; 6] = [
    "g_object_unref",
    "g_object_ref",
    "g_free",
    "g_list_free",
    "g_strfreev",
    "printf",
];
fn find_function(name: &str, env: &Env) -> Option<String> {
    let symbols = env.symbols.borrow();
    // if we can find the function in an object
    if let Some((obj_info, fn_info)) =
        env.analysis
            .find_object_by_function(env, |_| true, |f| f.glib_name == name)
    {
        let sym = symbols.by_tid(obj_info.type_id).unwrap(); // we are sure the object exists
        let (type_name, visible_type_name) = obj_info.generate_doc_link_info(fn_info);

        let name = sym.full_rust_name().replace(&obj_info.name, &type_name);
        Some(fn_info.doc_link(Some(&name), Some(&visible_type_name)))
    // or in a record
    } else if let Some((record_info, fn_info)) =
        env.analysis
            .find_record_by_function(env, |_| true, |f| f.glib_name == name)
    {
        let sym_name = symbols
            .by_tid(record_info.type_id)
            .unwrap()
            .full_rust_name(); // we are sure the object exists
        Some(fn_info.doc_link(Some(&sym_name), None))
    // or as a global function
    } else if let Some(fn_info) = env
        .analysis
        .find_global_function(env, |f| f.glib_name == name)
    {
        Some(fn_info.doc_link(None, None))
    } else {
        if !IGNORE_C_WARNING_FUNCS.contains(&name) {
            warn!("Function not found found: `{}`", name);
        }
        None
    }
}
<|endoftext|>


<commit_before>//! Capabilities to send data from operators
//!
//! Timely dataflow operators are only able to send data if they possess a "capability",
//! a system-created object which warns the runtime that the operator may still produce
//! output records.
//!
//! The timely dataflow runtime creates a capability and provides it to an operator whenever
//! the operator receives input data. The capabilities allow the operator to respond to the
//! received data, immediately or in the future, for as long as the capability is held.
//!
//! Timely dataflow's progress tracking infrastructure communicates the number of outstanding
//! capabilities across all workers.
//! Each operator may hold on to its capabilities, and may clone, advance, and drop them.
//! Each of these actions informs the timely dataflow runtime of changes to the number of outstanding
//! capabilities, so that the runtime can notice when the count for some capability reaches zero.
//! While an operator can hold capabilities indefinitely, and create as many new copies of them
//! as it would like, the progress tracking infrastructure will not move forward until the
//! operators eventually release their capabilities.
//!
//! Note that these capabilities currently lack the property of "transferability":
//! An operator should not hand its capabilities to some other operator. In the future, we should
//! probably bind capabilities more strongly to a specific operator and output.

use std::ops::Deref;
use std::rc::Rc;
use std::cell::RefCell;
use std::fmt::{self, Debug};

use order::PartialOrder;
use progress::Timestamp;
use progress::ChangeBatch;

/// The capability to send data with a certain timestamp on a dataflow edge.
///
/// Capabilities are used by timely dataflow's progress tracking machinery to restrict and track
/// when user code retains the ability to send messages on dataflow edges. All capabilities are
/// constructed by the system, and should eventually be dropped by the user. Failure to drop
/// a capability (for whatever reason) will cause timely dataflow's progress tracking to stall.
pub struct Capability<T: Timestamp> {
    time: T,
    internal: Rc<RefCell<ChangeBatch<T>>>,
}

impl<T: Timestamp> Capability<T> {
    /// The timestamp associated with this capability.
    #[inline]
    pub fn time(&self) -> &T {
        &self.time
    }

    /// Makes a new capability for a timestamp `new_time` greater or equal to the timestamp of
    /// the source capability (`self`).
    ///
    /// This method panics if `self.time` is not less or equal to `new_time`.
    #[inline]
    pub fn delayed(&self, new_time: &T) -> Capability<T> {
        if !self.time.less_equal(new_time) {
            panic!("Attempted to delay {:?} to {:?}, which is not `less_equal` the capability's time.", self, new_time);
        }
        mint(new_time.clone(), self.internal.clone())
    }

    /// Downgrades the capability to one corresponding to `new_time`.
    ///
    /// This method panics if `self.time` is not less or equal to `new_time`.
    #[inline]
    pub fn downgrade(&mut self, new_time: &T) {
        let new_cap = self.delayed(new_time);
        *self = new_cap;
    }
}

/// Creates a new capability at `t` while incrementing (and keeping a reference to) the provided
/// `ChangeBatch`.
/// Declared separately so that it can be kept private when `Capability` is re-exported.
pub fn mint<T: Timestamp>(time: T, internal: Rc<RefCell<ChangeBatch<T>>>) -> Capability<T> {
    internal.borrow_mut().update(time.clone(), 1);
    Capability {
        time: time,
        internal: internal
    }
}

// Necessary for correctness. When a capability is dropped, the "internal" `ChangeBatch` needs to be
// updated accordingly to inform the rest of the system that the operator has released its permit
// to send data and request notification at the associated timestamp.
impl<T: Timestamp> Drop for Capability<T> {
    fn drop(&mut self) {
        self.internal.borrow_mut().update(self.time.clone(), -1);
    }
}

impl<T: Timestamp> Clone for Capability<T> {
    fn clone(&self) -> Capability<T> {
        mint(self.time.clone(), self.internal.clone())
    }
}

impl<T: Timestamp> Deref for Capability<T> {
    type Target = T;
    fn deref(&self) -> &T {
        &self.time
    }
}

impl<T: Timestamp> Debug for Capability<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Capability {{ time: {:?}, internal: ... }}", self.time)
    }
}

impl<T: Timestamp> PartialEq for Capability<T> {
    fn eq(&self, other: &Self) -> bool {
        self.time() == other.time() && Rc::ptr_eq(&self.internal, &other.internal)
    }
}
impl<T: Timestamp> Eq for Capability<T> { }

impl<T: Timestamp> PartialOrder for Capability<T> {
    fn less_equal(&self, other: &Self) -> bool {
        self.time().less_equal(other.time()) && Rc::ptr_eq(&self.internal, &other.internal)
    }
}

/// A set of capabilities, for possibly incomparable times.
pub struct CapabilitySet<T: Timestamp> {
    elements: Vec<Capability<T>>,
}

impl<T: Timestamp> CapabilitySet<T> {

    /// Allocates an empty capability set.
    pub fn new() -> Self {
        CapabilitySet { elements: Vec::new() }
    }

    /// Inserts `capability` into the set, discarding redundant capabilities.
    pub fn insert(&mut self, capability: Capability<T>) {
        if !self.elements.iter().any(|c| c.less_equal(&capability)) {
            self.elements.retain(|c| !capability.less_equal(c));
            self.elements.push(capability);
        }
    }

    /// Creates a new capability to send data at `time`.
    ///
    /// This method panics if there does not exist a capability in `self.elements` less or equal to `time`.
    pub fn delayed(&self, time: &T) -> Capability<T> {
        self.elements.iter().find(|c| c.time().less_equal(time)).unwrap().delayed(time)
    }

    /// Downgrades the set of capabilities to correspond with the times in `frontier`.
    ///
    /// This method panics if any element of `frontier` is not greater or equal to some element of `self.elements`.
    pub fn downgrade(&mut self, frontier: &[T]) {
        let count = self.elements.len();
        for time in frontier.iter() {
            let capability = self.delayed(time);
            self.elements.push(capability);
        }
        self.elements.drain(..count);
    }
}<commit_msg>Add Hash impl<commit_after>//! Capabilities to send data from operators
//!
//! Timely dataflow operators are only able to send data if they possess a "capability",
//! a system-created object which warns the runtime that the operator may still produce
//! output records.
//!
//! The timely dataflow runtime creates a capability and provides it to an operator whenever
//! the operator receives input data. The capabilities allow the operator to respond to the
//! received data, immediately or in the future, for as long as the capability is held.
//!
//! Timely dataflow's progress tracking infrastructure communicates the number of outstanding
//! capabilities across all workers.
//! Each operator may hold on to its capabilities, and may clone, advance, and drop them.
//! Each of these actions informs the timely dataflow runtime of changes to the number of outstanding
//! capabilities, so that the runtime can notice when the count for some capability reaches zero.
//! While an operator can hold capabilities indefinitely, and create as many new copies of them
//! as it would like, the progress tracking infrastructure will not move forward until the
//! operators eventually release their capabilities.
//!
//! Note that these capabilities currently lack the property of "transferability":
//! An operator should not hand its capabilities to some other operator. In the future, we should
//! probably bind capabilities more strongly to a specific operator and output.

use std::ops::Deref;
use std::rc::Rc;
use std::cell::RefCell;
use std::fmt::{self, Debug};

use order::PartialOrder;
use progress::Timestamp;
use progress::ChangeBatch;

/// The capability to send data with a certain timestamp on a dataflow edge.
///
/// Capabilities are used by timely dataflow's progress tracking machinery to restrict and track
/// when user code retains the ability to send messages on dataflow edges. All capabilities are
/// constructed by the system, and should eventually be dropped by the user. Failure to drop
/// a capability (for whatever reason) will cause timely dataflow's progress tracking to stall.
pub struct Capability<T: Timestamp> {
    time: T,
    internal: Rc<RefCell<ChangeBatch<T>>>,
}

impl<T: Timestamp> Capability<T> {
    /// The timestamp associated with this capability.
    #[inline]
    pub fn time(&self) -> &T {
        &self.time
    }

    /// Makes a new capability for a timestamp `new_time` greater or equal to the timestamp of
    /// the source capability (`self`).
    ///
    /// This method panics if `self.time` is not less or equal to `new_time`.
    #[inline]
    pub fn delayed(&self, new_time: &T) -> Capability<T> {
        if !self.time.less_equal(new_time) {
            panic!("Attempted to delay {:?} to {:?}, which is not `less_equal` the capability's time.", self, new_time);
        }
        mint(new_time.clone(), self.internal.clone())
    }

    /// Downgrades the capability to one corresponding to `new_time`.
    ///
    /// This method panics if `self.time` is not less or equal to `new_time`.
    #[inline]
    pub fn downgrade(&mut self, new_time: &T) {
        let new_cap = self.delayed(new_time);
        *self = new_cap;
    }
}

/// Creates a new capability at `t` while incrementing (and keeping a reference to) the provided
/// `ChangeBatch`.
/// Declared separately so that it can be kept private when `Capability` is re-exported.
pub fn mint<T: Timestamp>(time: T, internal: Rc<RefCell<ChangeBatch<T>>>) -> Capability<T> {
    internal.borrow_mut().update(time.clone(), 1);
    Capability {
        time: time,
        internal: internal
    }
}

// Necessary for correctness. When a capability is dropped, the "internal" `ChangeBatch` needs to be
// updated accordingly to inform the rest of the system that the operator has released its permit
// to send data and request notification at the associated timestamp.
impl<T: Timestamp> Drop for Capability<T> {
    fn drop(&mut self) {
        self.internal.borrow_mut().update(self.time.clone(), -1);
    }
}

impl<T: Timestamp> Clone for Capability<T> {
    fn clone(&self) -> Capability<T> {
        mint(self.time.clone(), self.internal.clone())
    }
}

impl<T: Timestamp> Deref for Capability<T> {
    type Target = T;
    fn deref(&self) -> &T {
        &self.time
    }
}

impl<T: Timestamp> Debug for Capability<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Capability {{ time: {:?}, internal: ... }}", self.time)
    }
}

impl<T: Timestamp> PartialEq for Capability<T> {
    fn eq(&self, other: &Self) -> bool {
        self.time() == other.time() && Rc::ptr_eq(&self.internal, &other.internal)
    }
}
impl<T: Timestamp> Eq for Capability<T> { }

impl<T: Timestamp> PartialOrder for Capability<T> {
    fn less_equal(&self, other: &Self) -> bool {
        self.time().less_equal(other.time()) && Rc::ptr_eq(&self.internal, &other.internal)
    }
}

impl<T: Timestamp> ::std::hash::Hash for Capability<T> {
    fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
        self.time.hash(state);
    }
}

/// A set of capabilities, for possibly incomparable times.
pub struct CapabilitySet<T: Timestamp> {
    elements: Vec<Capability<T>>,
}

impl<T: Timestamp> CapabilitySet<T> {

    /// Allocates an empty capability set.
    pub fn new() -> Self {
        CapabilitySet { elements: Vec::new() }
    }

    /// Inserts `capability` into the set, discarding redundant capabilities.
    pub fn insert(&mut self, capability: Capability<T>) {
        if !self.elements.iter().any(|c| c.less_equal(&capability)) {
            self.elements.retain(|c| !capability.less_equal(c));
            self.elements.push(capability);
        }
    }

    /// Creates a new capability to send data at `time`.
    ///
    /// This method panics if there does not exist a capability in `self.elements` less or equal to `time`.
    pub fn delayed(&self, time: &T) -> Capability<T> {
        self.elements.iter().find(|c| c.time().less_equal(time)).unwrap().delayed(time)
    }

    /// Downgrades the set of capabilities to correspond with the times in `frontier`.
    ///
    /// This method panics if any element of `frontier` is not greater or equal to some element of `self.elements`.
    pub fn downgrade(&mut self, frontier: &[T]) {
        let count = self.elements.len();
        for time in frontier.iter() {
            let capability = self.delayed(time);
            self.elements.push(capability);
        }
        self.elements.drain(..count);
    }
}<|endoftext|>


<commit_before>use async_trait::async_trait;
use serenity::builder::CreateApplicationCommand;
use serenity::client::Context;

use crate::config::Config;
use crate::discord::slash_commands::SlashCommand;
use serenity::model::interactions::application_command::ApplicationCommandInteraction;
use serenity::model::interactions::message_component::ButtonStyle;
use serenity::model::interactions::{
    InteractionApplicationCommandCallbackDataFlags, InteractionResponseType,
};
use tracing::error;
use typemap_rev::TypeMap;

const SUPPORT_SERVER_INVITE_LINK: &str = "https://discord.io/safetyjim";
const JIM_INVITE_LINK: &str = "https://discord.com/api/oauth2/authorize?client_id=881152939530534913&permissions=0&scope=bot%20applications.commands";

pub struct InviteCommand;

#[async_trait]
impl SlashCommand for InviteCommand {
    fn command_name(&self) -> &'static str {
        "invite"
    }

    fn create_command<'a>(
        &self,
        command: &'a mut CreateApplicationCommand,
    ) -> &'a mut CreateApplicationCommand {
        command
            .name("invite")
            .description("displays links to invite Jim and get support")
            .default_permission(true)
    }

    async fn handle_command(
        &self,
        context: &Context,
        interaction: &ApplicationCommandInteraction,
        _config: &Config,
        _services: &TypeMap,
    ) -> anyhow::Result<()> {
        interaction
            .create_interaction_response(&context.http, |response| {
                response
                    .kind(InteractionResponseType::ChannelMessageWithSource)
                    .interaction_response_data(|message| {
                        message
                            .content("Links:")
                            .components(|components| {
                                components.create_action_row(|row| {
                                    row.create_button(|button| {
                                        button
                                            .label("Invite Jim!")
                                            .url(JIM_INVITE_LINK)
                                            .style(ButtonStyle::Link)
                                    })
                                    .create_button(|button| {
                                        button
                                            .label("Join our support server!")
                                            .url(SUPPORT_SERVER_INVITE_LINK)
                                            .style(ButtonStyle::Link)
                                    })
                                })
                            })
                            .flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL)
                    })
            })
            .await
            .map_err(|err| {
                error!("failed to reply to interaction {}", err);
                err
            })?;

        Ok(())
    }
}
<commit_msg>Make invite command response visible<commit_after>use async_trait::async_trait;
use serenity::builder::CreateApplicationCommand;
use serenity::client::Context;

use crate::config::Config;
use crate::discord::slash_commands::SlashCommand;
use serenity::model::interactions::application_command::ApplicationCommandInteraction;
use serenity::model::interactions::message_component::ButtonStyle;
use serenity::model::interactions::{
    InteractionApplicationCommandCallbackDataFlags, InteractionResponseType,
};
use tracing::error;
use typemap_rev::TypeMap;

const SUPPORT_SERVER_INVITE_LINK: &str = "https://discord.io/safetyjim";
const JIM_INVITE_LINK: &str = "https://discord.com/api/oauth2/authorize?client_id=881152939530534913&permissions=0&scope=bot%20applications.commands";

pub struct InviteCommand;

#[async_trait]
impl SlashCommand for InviteCommand {
    fn command_name(&self) -> &'static str {
        "invite"
    }

    fn create_command<'a>(
        &self,
        command: &'a mut CreateApplicationCommand,
    ) -> &'a mut CreateApplicationCommand {
        command
            .name("invite")
            .description("displays links to invite Jim and get support")
            .default_permission(true)
    }

    async fn handle_command(
        &self,
        context: &Context,
        interaction: &ApplicationCommandInteraction,
        _config: &Config,
        _services: &TypeMap,
    ) -> anyhow::Result<()> {
        interaction
            .create_interaction_response(&context.http, |response| {
                response
                    .kind(InteractionResponseType::ChannelMessageWithSource)
                    .interaction_response_data(|message| {
                        message
                            .content("Links:")
                            .components(|components| {
                                components.create_action_row(|row| {
                                    row.create_button(|button| {
                                        button
                                            .label("Invite Jim!")
                                            .url(JIM_INVITE_LINK)
                                            .style(ButtonStyle::Link)
                                    })
                                    .create_button(|button| {
                                        button
                                            .label("Join our support server!")
                                            .url(SUPPORT_SERVER_INVITE_LINK)
                                            .style(ButtonStyle::Link)
                                    })
                                })
                            })
                    })
            })
            .await
            .map_err(|err| {
                error!("failed to reply to interaction {}", err);
                err
            })?;

        Ok(())
    }
}
<|endoftext|>


<commit_before>#[derive(Debug, Default)]
pub struct RegConfig {
    // EP
    data_transfer_pattern: DataTransferPattern,

    // BE
    endianness: Endianness,

    cu: bool,
    kseg0_cache_bits: [bool;3],
    kseg0_cache_enabled: bool
}

impl From<u32> for RegConfig {
    fn from(value: u32) -> Self {
        RegConfig {
            data_transfer_pattern: value.into(),

            endianness: value.into(),

            cu: (value & (1 << 3)) != 0,
            kseg0_cache_bits: [
                (value & (1 << 0)) != 0,
                (value & (1 << 1)) != 0,
                (value & (1 << 2)) != 0,
            ],
            // TODO: Extract to method?
            kseg0_cache_enabled: value & 0b111 != 0b010
        }
    }
}

#[derive(Debug)]
enum DataTransferPattern {
    Normal, // D
    DxxDxx
}

impl Default for DataTransferPattern {
    fn default() -> Self {
        DataTransferPattern::Normal
    }
}

impl From<u32> for DataTransferPattern {
    fn from(value: u32) -> Self {
        match (value >> 24) & 0b1111 {
            0 => DataTransferPattern::Normal,
            6 => DataTransferPattern::DxxDxx,
            _ => panic!("Invalid data transfer pattern (EP): {:#x}", value)
        }
    }
}

#[derive(Debug)]
enum Endianness {
    Little,
    Big
}

impl Default for Endianness {
    fn default() -> Self {
        Endianness::Big
    }
}

impl From<u32> for Endianness {
    fn from(value: u32) -> Self {
        match (value >> 15) & 0b1 {
            0 => Endianness::Little,
            1 => Endianness::Big,
            _ => unreachable!()
        }
    }
}
<commit_msg>Minor clarity change<commit_after>#[derive(Debug, Default)]
pub struct RegConfig {
    // EP
    data_transfer_pattern: DataTransferPattern,

    // BE
    endianness: Endianness,

    cu: bool,
    kseg0_cache_enable_bits: [bool;3],
    kseg0_cache_enabled: bool
}

impl From<u32> for RegConfig {
    fn from(value: u32) -> Self {
        RegConfig {
            data_transfer_pattern: value.into(),

            endianness: value.into(),

            cu: (value & (1 << 3)) != 0,
            kseg0_cache_enable_bits: [
                (value & (1 << 0)) != 0,
                (value & (1 << 1)) != 0,
                (value & (1 << 2)) != 0,
            ],
            // TODO: Extract to method?
            kseg0_cache_enabled: value & 0b111 != 0b010
        }
    }
}

#[derive(Debug)]
enum DataTransferPattern {
    Normal, // D
    DxxDxx
}

impl Default for DataTransferPattern {
    fn default() -> Self {
        DataTransferPattern::Normal
    }
}

impl From<u32> for DataTransferPattern {
    fn from(value: u32) -> Self {
        match (value >> 24) & 0b1111 {
            0 => DataTransferPattern::Normal,
            6 => DataTransferPattern::DxxDxx,
            _ => panic!("Invalid data transfer pattern (EP): {:#x}", value)
        }
    }
}

#[derive(Debug)]
enum Endianness {
    Little,
    Big
}

impl Default for Endianness {
    fn default() -> Self {
        Endianness::Big
    }
}

impl From<u32> for Endianness {
    fn from(value: u32) -> Self {
        match (value >> 15) & 0b1 {
            0 => Endianness::Little,
            1 => Endianness::Big,
            _ => unreachable!()
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;

use context::ContextMap;
use dispatcher::demux::Demultiplexer;
use dispatcher::request::{RequestHandle, Request};
use reactor::{Event, EventDemultiplexer, EventHandler, Reactor, SharedData};
use dispatcher::response::ResponseSender;

#[allow(type_complexity)]
pub struct RequestReactor {
    handlers: BTreeMap<RequestHandle, Box<for<'a> EventHandler<Request, SharedData<'a>>>>,
    demultiplexer: Demultiplexer<Request>,
    context_map: ContextMap,
    responder: Box<ResponseSender>,
}

impl RequestReactor {
    pub fn new(demultiplexer: Demultiplexer<Request>,
               context_map: ContextMap,
               responder: Box<ResponseSender>)
               -> RequestReactor {
        RequestReactor {
            demultiplexer: demultiplexer,
            context_map: context_map,
            handlers: BTreeMap::new(),
            responder: responder,
        }
    }
}

impl Reactor for RequestReactor {
    type Event = Request;
    fn handle_events(&mut self) {
        let mut shared_data = SharedData::new(&mut self.context_map, &mut *self.responder);
        while let Some(request) = self.demultiplexer.select() {
            trace!("RequestReactor: got event");
            if let Some(handler) = self.handlers.get_mut(&request.handle()) {
                handler.handle_event(request, &mut shared_data);
            } else {
                trace!("RequestReactor: no handler found for event");
            }
        }
    }
    fn register_handler(&mut self,
                        handler: Box<for<'a> EventHandler<Self::Event, SharedData<'a>>>) {
        self.handlers.insert(handler.handle(), handler);
    }
    fn remove_handler_by_handle(&mut self, handler: &RequestHandle) {
        self.handlers.remove(handler);
    }
}
<commit_msg>RequestReactor: make context_map field public<commit_after>use std::collections::BTreeMap;

use context::ContextMap;
use dispatcher::demux::Demultiplexer;
use dispatcher::request::{RequestHandle, Request};
use reactor::{Event, EventDemultiplexer, EventHandler, Reactor, SharedData};
use dispatcher::response::ResponseSender;

#[allow(type_complexity)]
pub struct RequestReactor {
    handlers: BTreeMap<RequestHandle, Box<for<'a> EventHandler<Request, SharedData<'a>>>>,
    demultiplexer: Demultiplexer<Request>,
    pub context_map: ContextMap,
    responder: Box<ResponseSender>,
}

impl RequestReactor {
    pub fn new(demultiplexer: Demultiplexer<Request>,
               context_map: ContextMap,
               responder: Box<ResponseSender>)
               -> RequestReactor {
        RequestReactor {
            demultiplexer: demultiplexer,
            context_map: context_map,
            handlers: BTreeMap::new(),
            responder: responder,
        }
    }
}

impl Reactor for RequestReactor {
    type Event = Request;
    fn handle_events(&mut self) {
        let mut shared_data = SharedData::new(&mut self.context_map, &mut *self.responder);
        while let Some(request) = self.demultiplexer.select() {
            trace!("RequestReactor: got event");
            if let Some(handler) = self.handlers.get_mut(&request.handle()) {
                handler.handle_event(request, &mut shared_data);
            } else {
                trace!("RequestReactor: no handler found for event");
            }
        }
    }
    fn register_handler(&mut self,
                        handler: Box<for<'a> EventHandler<Self::Event, SharedData<'a>>>) {
        self.handlers.insert(handler.handle(), handler);
    }
    fn remove_handler_by_handle(&mut self, handler: &RequestHandle) {
        self.handlers.remove(handler);
    }
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::collections::BTreeMap;
use std::error::Error;

use ruru::{Class, Object, AnyObject, Boolean, RString, VM, Hash, NilClass, VerifiedObject};

use libimagstore::store::EntryHeader;
use libimagstore::store::EntryContent;
use libimagstore::store::Entry;
use libimagstore::storeid::StoreId;

use ruby_utils::IntoToml;
use toml_utils::IntoRuby;
use util::Wrap;
use util::Unwrap;
use cache::StoreHandle;

pub struct FileLockEntryHandle(StoreHandle, StoreId);

impl FileLockEntryHandle {
    pub fn new(sh: StoreHandle, id: StoreId) -> FileLockEntryHandle {
        FileLockEntryHandle(sh, id)
    }

    pub fn store_handle(&self) -> &StoreHandle {
        &self.0
    }

    pub fn fle_handle(&self) -> &StoreId {
        &self.1
    }
}

wrappable_struct!(FileLockEntryHandle, FileLockEntryWrapper, FLE_WRAPPER);
class!(RFileLockEntryHandle);
impl_wrap!(FileLockEntryHandle => FLE_WRAPPER);
impl_unwrap!(RFileLockEntryHandle => FileLockEntryHandle => FLE_WRAPPER);
impl_verified_object!(RFileLockEntryHandle);

/// Helper macro for operating on RUBY_STORE_CACHE object
///
/// This helps us calling operations on FileLockEntry objects.
///
/// What I do here: Fetch the Store object from the cache, fetch the appropriate FileLockEntry and
/// call the operation on it.
///
/// This could be improved with another cache, so not the store is cached but the FileLockEntry
/// only, but then we run into lifetime problems with the Store and its FileLockEntry objects.
/// Feel free to fix this, but for now, this is a workable solution.
///
#[macro_export]
macro_rules! call_on_fle_from_store {
    ($itself:ident ($wrapper:ident) -> $name:ident -> $operation:block) => {{
        let handle = $itself.get_data(&*$wrapper);
        let store_handle = handle.store_handle();
        call_on_store_by_handle! {
            store_handle named store inside {
                match store.get(handle.fle_handle().clone()) {
                    Ok(Some(mut $name)) => {
                        $operation
                    },
                    Ok(None) => {
                        VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
                        NilClass::new().to_any_object()
                    },
                    Err(e) => {
                        VM::raise(Class::from_existing("RuntimeError"), e.description());
                        NilClass::new().to_any_object()
                    },
                }
            }
        }
    }};
    ($itself:ident ($wrapper:ident) -> $name:ident -> $operation: block on fail return $ex:expr) => {{
        let handle = $itself.get_data(&*$wrapper);
        let store_handle = handle.store_handle();
        call_on_store_by_handle! {
            store_handle named store inside {
                match store.get(handle.fle_handle().clone()) {
                    Ok(Some(mut $name)) => {
                        $operation
                    },
                    Ok(None) => {
                        VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
                        $ex
                    },
                    Err(e) => {
                        VM::raise(Class::from_existing("RuntimeError"), e.description());
                        $ex
                    },
                }
            }
        }
    }};
}

methods!(
    RFileLockEntryHandle,
    itself,

    fn r_get_location() -> AnyObject {
        call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> { fle.get_location().clone().wrap() })
    }

    fn r_get_header() -> AnyObject {
        call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> { fle.get_header().clone().wrap() })
    }

    fn r_set_header(hdr: Hash) -> NilClass {
        use ruby_utils::IntoToml;
        use toml::Value;

        let entryheader = match typecheck!(hdr or return NilClass::new()).into_toml() {
            Value::Table(t) => EntryHeader::from(t),
            _ => {
                let ec = Class::from_existing("RuntimeError");
                VM::raise(ec, "Something weird happened. Hash seems to be not a Hash");
                return NilClass::new();
            },
        };

        call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> {
            *fle.get_header_mut() = entryheader;
            NilClass::new().to_any_object()
        });

        NilClass::new()
    }

    fn r_get_content() -> AnyObject {
        call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> {
            fle.get_content().clone().wrap()
        } on fail return NilClass::new().to_any_object())
    }

    fn r_set_content(ctt: RString) -> NilClass {
        use ruby_utils::IntoToml;
        use toml::Value;

        let content = match typecheck!(ctt).into_toml() {
            Value::String(s) => s,
            _ => {
                let ec = Class::from_existing("RuntimeError");
                VM::raise(ec, "Something weird happened. String seems to be not a String");
                return NilClass::new();
            },
        };

        call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> {
            *fle.get_content_mut() = content;
            NilClass::new().to_any_object()
        });

        NilClass::new()
    }

);

wrappable_struct!(EntryHeader, EntryHeaderWrapper, ENTRY_HEADER_WRAPPER);
class!(REntryHeader);
impl_wrap!(EntryHeader => ENTRY_HEADER_WRAPPER);
impl_unwrap!(REntryHeader => EntryHeader => ENTRY_HEADER_WRAPPER);
impl_verified_object!(REntryHeader);

methods!(
    REntryHeader,
    itself,

    fn r_entry_header_new() -> AnyObject {
        EntryHeader::new().wrap()
    }

    fn r_entry_header_insert(spec: RString, obj: AnyObject) -> Boolean {
        let spec = typecheck!(spec or return Boolean::new(false)).to_string();
        let obj = obj.unwrap(); // possibly not safe... TODO

        match itself.get_data(&*ENTRY_HEADER_WRAPPER).insert(&spec, obj.into_toml()) {
            Ok(b) => Boolean::new(b),
            Err(e) => {
                VM::raise(Class::from_existing("RuntimeError"), e.description());
                Boolean::new(false)
            }
        }
    }

    fn r_entry_header_set(spec: RString, obj: AnyObject) -> AnyObject {
        use ruru::NilClass;

        let spec = typecheck!(spec or return any Boolean::new(false)).to_string();
        let obj = obj.unwrap(); // possibly not safe... TODO

        match itself.get_data(&*ENTRY_HEADER_WRAPPER).set(&spec, obj.into_toml()) {
            Ok(Some(v)) => v.into_ruby(),
            Ok(None)    => NilClass::new().to_any_object(),
            Err(e) => {
                VM::raise(Class::from_existing("RuntimeError"), e.description());
                return Boolean::new(false).to_any_object();
            }
        }
    }

    fn r_entry_header_get(spec: RString) -> AnyObject {
        use ruru::NilClass;

        let spec = typecheck!(spec or return any Boolean::new(false)).to_string();

        match itself.get_data(&*ENTRY_HEADER_WRAPPER).read(&spec) {
            Ok(Some(v)) => v.into_ruby(),
            Ok(None)    => NilClass::new().to_any_object(),
            Err(e) => {
                VM::raise(Class::from_existing("RuntimeError"), e.description());
                return Boolean::new(false).to_any_object();
            }
        }
    }

);

wrappable_struct!(EntryContent, EntryContentWrapper, ENTRY_CONTENT_WRAPPER);
class!(REntryContent);
impl_wrap!(EntryContent => ENTRY_CONTENT_WRAPPER);
impl_unwrap!(REntryContent => EntryContent => ENTRY_CONTENT_WRAPPER);

wrappable_struct!(Entry, EntryWrapper, ENTRY_WRAPPER);
class!(REntry);
impl_unwrap!(REntry => Entry => ENTRY_WRAPPER);

pub fn setup_filelockentry() -> Class {
    let mut class = Class::new("RFileLockEntryHandle", None);
    class.define(|itself| {
        itself.def("location", r_get_location);
        itself.def("header"  , r_get_header);
        itself.def("header=" , r_set_header);
        itself.def("content" , r_get_content);
        itself.def("content=", r_set_content);
    });
    class
}

pub fn setup_entryheader() -> Class {
    let mut class = Class::new("REntryHeader", None);
    class.define(|itself| {
        itself.def("insert", r_entry_header_insert);
        itself.def("set"   , r_entry_header_set);
        itself.def("[]="   , r_entry_header_set);
        itself.def("read"  , r_entry_header_get);
        itself.def("[]"    , r_entry_header_get);
    });
    class
}

pub fn setup_entrycontent() -> Class {
    let string = Class::from_existing("String");
    let mut class = Class::new("REntryContent", Some(&string));
    class
}
<commit_msg>Remove unnecessary "mut"<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::collections::BTreeMap;
use std::error::Error;

use ruru::{Class, Object, AnyObject, Boolean, RString, VM, Hash, NilClass, VerifiedObject};

use libimagstore::store::EntryHeader;
use libimagstore::store::EntryContent;
use libimagstore::store::Entry;
use libimagstore::storeid::StoreId;

use ruby_utils::IntoToml;
use toml_utils::IntoRuby;
use util::Wrap;
use util::Unwrap;
use cache::StoreHandle;

pub struct FileLockEntryHandle(StoreHandle, StoreId);

impl FileLockEntryHandle {
    pub fn new(sh: StoreHandle, id: StoreId) -> FileLockEntryHandle {
        FileLockEntryHandle(sh, id)
    }

    pub fn store_handle(&self) -> &StoreHandle {
        &self.0
    }

    pub fn fle_handle(&self) -> &StoreId {
        &self.1
    }
}

wrappable_struct!(FileLockEntryHandle, FileLockEntryWrapper, FLE_WRAPPER);
class!(RFileLockEntryHandle);
impl_wrap!(FileLockEntryHandle => FLE_WRAPPER);
impl_unwrap!(RFileLockEntryHandle => FileLockEntryHandle => FLE_WRAPPER);
impl_verified_object!(RFileLockEntryHandle);

/// Helper macro for operating on RUBY_STORE_CACHE object
///
/// This helps us calling operations on FileLockEntry objects.
///
/// What I do here: Fetch the Store object from the cache, fetch the appropriate FileLockEntry and
/// call the operation on it.
///
/// This could be improved with another cache, so not the store is cached but the FileLockEntry
/// only, but then we run into lifetime problems with the Store and its FileLockEntry objects.
/// Feel free to fix this, but for now, this is a workable solution.
///
#[macro_export]
macro_rules! call_on_fle_from_store {
    ($itself:ident ($wrapper:ident) -> $name:ident -> $operation:block) => {{
        let handle = $itself.get_data(&*$wrapper);
        let store_handle = handle.store_handle();
        call_on_store_by_handle! {
            store_handle named store inside {
                match store.get(handle.fle_handle().clone()) {
                    Ok(Some(mut $name)) => {
                        $operation
                    },
                    Ok(None) => {
                        VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
                        NilClass::new().to_any_object()
                    },
                    Err(e) => {
                        VM::raise(Class::from_existing("RuntimeError"), e.description());
                        NilClass::new().to_any_object()
                    },
                }
            }
        }
    }};
    ($itself:ident ($wrapper:ident) -> $name:ident -> $operation: block on fail return $ex:expr) => {{
        let handle = $itself.get_data(&*$wrapper);
        let store_handle = handle.store_handle();
        call_on_store_by_handle! {
            store_handle named store inside {
                match store.get(handle.fle_handle().clone()) {
                    Ok(Some(mut $name)) => {
                        $operation
                    },
                    Ok(None) => {
                        VM::raise(Class::from_existing("RuntimeError"), "Obj does not exist");
                        $ex
                    },
                    Err(e) => {
                        VM::raise(Class::from_existing("RuntimeError"), e.description());
                        $ex
                    },
                }
            }
        }
    }};
}

methods!(
    RFileLockEntryHandle,
    itself,

    fn r_get_location() -> AnyObject {
        call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> { fle.get_location().clone().wrap() })
    }

    fn r_get_header() -> AnyObject {
        call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> { fle.get_header().clone().wrap() })
    }

    fn r_set_header(hdr: Hash) -> NilClass {
        use ruby_utils::IntoToml;
        use toml::Value;

        let entryheader = match typecheck!(hdr or return NilClass::new()).into_toml() {
            Value::Table(t) => EntryHeader::from(t),
            _ => {
                let ec = Class::from_existing("RuntimeError");
                VM::raise(ec, "Something weird happened. Hash seems to be not a Hash");
                return NilClass::new();
            },
        };

        call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> {
            *fle.get_header_mut() = entryheader;
            NilClass::new().to_any_object()
        });

        NilClass::new()
    }

    fn r_get_content() -> AnyObject {
        call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> {
            fle.get_content().clone().wrap()
        } on fail return NilClass::new().to_any_object())
    }

    fn r_set_content(ctt: RString) -> NilClass {
        use ruby_utils::IntoToml;
        use toml::Value;

        let content = match typecheck!(ctt).into_toml() {
            Value::String(s) => s,
            _ => {
                let ec = Class::from_existing("RuntimeError");
                VM::raise(ec, "Something weird happened. String seems to be not a String");
                return NilClass::new();
            },
        };

        call_on_fle_from_store!(itself (FLE_WRAPPER) -> fle -> {
            *fle.get_content_mut() = content;
            NilClass::new().to_any_object()
        });

        NilClass::new()
    }

);

wrappable_struct!(EntryHeader, EntryHeaderWrapper, ENTRY_HEADER_WRAPPER);
class!(REntryHeader);
impl_wrap!(EntryHeader => ENTRY_HEADER_WRAPPER);
impl_unwrap!(REntryHeader => EntryHeader => ENTRY_HEADER_WRAPPER);
impl_verified_object!(REntryHeader);

methods!(
    REntryHeader,
    itself,

    fn r_entry_header_new() -> AnyObject {
        EntryHeader::new().wrap()
    }

    fn r_entry_header_insert(spec: RString, obj: AnyObject) -> Boolean {
        let spec = typecheck!(spec or return Boolean::new(false)).to_string();
        let obj = obj.unwrap(); // possibly not safe... TODO

        match itself.get_data(&*ENTRY_HEADER_WRAPPER).insert(&spec, obj.into_toml()) {
            Ok(b) => Boolean::new(b),
            Err(e) => {
                VM::raise(Class::from_existing("RuntimeError"), e.description());
                Boolean::new(false)
            }
        }
    }

    fn r_entry_header_set(spec: RString, obj: AnyObject) -> AnyObject {
        use ruru::NilClass;

        let spec = typecheck!(spec or return any Boolean::new(false)).to_string();
        let obj = obj.unwrap(); // possibly not safe... TODO

        match itself.get_data(&*ENTRY_HEADER_WRAPPER).set(&spec, obj.into_toml()) {
            Ok(Some(v)) => v.into_ruby(),
            Ok(None)    => NilClass::new().to_any_object(),
            Err(e) => {
                VM::raise(Class::from_existing("RuntimeError"), e.description());
                return Boolean::new(false).to_any_object();
            }
        }
    }

    fn r_entry_header_get(spec: RString) -> AnyObject {
        use ruru::NilClass;

        let spec = typecheck!(spec or return any Boolean::new(false)).to_string();

        match itself.get_data(&*ENTRY_HEADER_WRAPPER).read(&spec) {
            Ok(Some(v)) => v.into_ruby(),
            Ok(None)    => NilClass::new().to_any_object(),
            Err(e) => {
                VM::raise(Class::from_existing("RuntimeError"), e.description());
                return Boolean::new(false).to_any_object();
            }
        }
    }

);

wrappable_struct!(EntryContent, EntryContentWrapper, ENTRY_CONTENT_WRAPPER);
class!(REntryContent);
impl_wrap!(EntryContent => ENTRY_CONTENT_WRAPPER);
impl_unwrap!(REntryContent => EntryContent => ENTRY_CONTENT_WRAPPER);

wrappable_struct!(Entry, EntryWrapper, ENTRY_WRAPPER);
class!(REntry);
impl_unwrap!(REntry => Entry => ENTRY_WRAPPER);

pub fn setup_filelockentry() -> Class {
    let mut class = Class::new("RFileLockEntryHandle", None);
    class.define(|itself| {
        itself.def("location", r_get_location);
        itself.def("header"  , r_get_header);
        itself.def("header=" , r_set_header);
        itself.def("content" , r_get_content);
        itself.def("content=", r_set_content);
    });
    class
}

pub fn setup_entryheader() -> Class {
    let mut class = Class::new("REntryHeader", None);
    class.define(|itself| {
        itself.def("insert", r_entry_header_insert);
        itself.def("set"   , r_entry_header_set);
        itself.def("[]="   , r_entry_header_set);
        itself.def("read"  , r_entry_header_get);
        itself.def("[]"    , r_entry_header_get);
    });
    class
}

pub fn setup_entrycontent() -> Class {
    let string = Class::from_existing("String");
    Class::new("REntryContent", Some(&string))
}
<|endoftext|>


<commit_before>/*
 * Implemenation of a bitfield as a vec. Only
 * supports bytes (Vec<u8>) as the underlying
 * storage.
 *
 * A future implementation should be more efficient,
 * this is just to get the job done for now.
 */
extern crate ssz;

use std::cmp::max;

#[derive(Eq, Clone, Default, Debug)]
pub struct BooleanBitfield{
    len: usize,
    vec: Vec<u8>
}

impl BooleanBitfield {
    /// Create a new bitfield with a length of zero.
    pub fn new() -> Self {
        Self {
            len: 0,
            vec: vec![0]
        }
    }

    /// Create a new bitfield of a certain capacity
    pub fn with_capacity(capacity: usize) -> Self {
        let mut vec = Vec::with_capacity(capacity / 8 + 1);
        vec.push(0);
        Self {
            len: 0,
            vec
        }
    }

    /// Read the value of a bit.
    ///
    /// Will return `true` if the bit has been set to `true`
    /// without then being set to `False`.
    pub fn get_bit(&self, i: usize) -> bool {
        let bit = |i: usize| i % 8;
        let byte = |i: usize| i / 8;

        if byte(i) >= self.vec.len() {
            false
        } else {
             self.vec[byte(i)] & (1 << (bit(i) as u8)) != 0
        }
    }

    /// Set the value of a bit.
    ///
    /// If this bit is larger than the length of the underlying byte
    /// array it will be extended.
    pub fn set_bit(&mut self, i: usize, to: bool) {
        let bit = |i: usize| i % 8;
        let byte = |i: usize| i / 8;

        self.len = max(self.len, i + 1);

        if byte(i) >= self.vec.len() {
            self.vec.resize(byte(i) + 1, 0);
        }
        if to {
            self.vec[byte(i)] =
                self.vec[byte(i)] |  (1 << (bit(i) as u8))
        } else {
            self.vec[byte(i)] =
                self.vec[byte(i)] & !(1 << (bit(i) as u8))
        }
    }

    /// Return the "length" of this bitfield. Length is defined as
    /// the highest bit that has been set.
    ///
    /// Note: this is distinct from the length of the underlying
    /// vector.
    pub fn len(&self) -> usize { self.len }

    /// True if no bits have ever been set. A bit that is set and then
    /// unset will still count to the length of the bitfield.
    ///
    /// Note: this is distinct from the length of the underlying
    /// vector.
    pub fn is_empty(&self) -> bool { self.len == 0 }

    /// Iterate through the underlying vector and count the number of
    /// true bits.
    pub fn num_true_bits(&self) -> u64 {
        let mut count: u64 = 0;
        for byte in &self.vec {
            for bit in 0..8 {
                if byte & (1 << (bit as u8)) != 0 {
                    count += 1;
                }
            }
        }
        count
    }

    /// Iterate through the underlying vector and find the highest
    /// set bit. Useful for instantiating a new instance from
    /// some set of bytes.
    pub fn compute_length(bytes: &[u8]) -> usize {
        for byte in (0..bytes.len()).rev() {
            for bit in (0..8).rev() {
                if byte & (1 << (bit as u8)) != 0 {
                    return (byte * 8) + bit
                }
            }
        }
        0
    }

    /// Clone and return the underlying byte array (`Vec<u8>`).
    pub fn to_be_vec(&self) -> Vec<u8> {
        let mut o = self.vec.clone();
        o.reverse();
        o
    }
}

impl<'a> From<&'a [u8]> for BooleanBitfield {
    fn from(input: &[u8]) -> Self {
        let mut vec = input.to_vec();
        vec.reverse();
        BooleanBitfield {
            vec,
            len: BooleanBitfield::compute_length(input)
        }
    }
}

impl PartialEq for BooleanBitfield {
    fn eq(&self, other: &BooleanBitfield) -> bool {
        (self.vec == other.vec) &
            (self.len == other.len)
    }
}

impl ssz::Encodable for BooleanBitfield {
    fn ssz_append(&self, s: &mut ssz::SszStream) {
        s.append_vec(&self.to_be_vec());
    }
}

impl ssz::Decodable for BooleanBitfield {
    fn ssz_decode(bytes: &[u8], index: usize)
        -> Result<(Self, usize), ssz::DecodeError>
    {
        let len = ssz::decode::decode_length(
            bytes,
            index,
            ssz::LENGTH_BYTES)?;
        if (ssz::LENGTH_BYTES + len) > bytes.len() {
            return Err(ssz::DecodeError::TooShort);
        }
        if len == 0 {
            Ok((BooleanBitfield::new(),
                index + ssz::LENGTH_BYTES))
        } else {
            let b = BooleanBitfield::from(&bytes[(index + 4)..(len + 4)]);
            let index = index + ssz::LENGTH_BYTES + len;
            Ok((b, index))
        }
    }
}

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

    #[test]
    fn test_ssz_encoding() {
        let mut b = BooleanBitfield::new();
        b.set_bit(8, true);

        let mut stream = ssz::SszStream::new();
        stream.append(&b);

        assert_eq!(stream.drain(), vec![0, 0, 0, 2, 1, 0]);
    }

    #[test]
    fn test_ssz_decoding() {
        /*
         * Correct input
         */
        let input = vec![0, 0, 0, 2, 1, 0];
        let (b, i) = BooleanBitfield::ssz_decode(&input, 0).unwrap();
        assert_eq!(i, 6);
        assert_eq!(b.num_true_bits(), 1);
        assert_eq!(b.get_bit(8), true);

        /*
         * Input too long
         */
        let mut input = vec![0, 0, 0, 2, 1, 0];
        input.push(42);
        let (b, i) = BooleanBitfield::ssz_decode(&input, 0).unwrap();
        assert_eq!(i, 6);
        assert_eq!(b.num_true_bits(), 1);
        assert_eq!(b.get_bit(8), true);

        /*
         * Input too short
         */
        let input = vec![0, 0, 0, 2, 1];
        let res = BooleanBitfield::ssz_decode(&input, 0);
        assert_eq!(res, Err(ssz::DecodeError::TooShort));
    }

    #[test]
    fn test_new_bitfield_len() {
        let b = BooleanBitfield::new();
        assert_eq!(b.len(), 0);
        assert_eq!(b.to_be_vec(), vec![0]);

        let b = BooleanBitfield::with_capacity(100);
        assert_eq!(b.len(), 0);
        assert_eq!(b.to_be_vec(), vec![0]);
    }

    #[test]
    fn test_new_bitfield_len() {
        let b = BooleanBitfield::new();
        assert_eq!(b.len(), 0);
        assert_eq!(b.to_be_vec(), vec![0]);

        let b = BooleanBitfield::with_capacity(100);
        assert_eq!(b.len(), 0);
        assert_eq!(b.to_be_vec(), vec![0]);
    }

    #[test]
    fn test_bitfield_set() {
        let mut b = BooleanBitfield::new();
        b.set_bit(0, false);
        assert_eq!(b.to_be_vec(), [0]);

        b = BooleanBitfield::new();
        b.set_bit(7, true);
        assert_eq!(b.to_be_vec(), [128]);
        b.set_bit(7, false);
        assert_eq!(b.to_be_vec(), [0]);
        assert_eq!(b.len(), 8);

        b = BooleanBitfield::new();
        b.set_bit(7, true);
        b.set_bit(0, true);
        assert_eq!(b.to_be_vec(), [129]);
        b.set_bit(7, false);
        assert_eq!(b.to_be_vec(), [1]);
        assert_eq!(b.len(), 8);

        b = BooleanBitfield::new();
        b.set_bit(8, true);
        assert_eq!(b.to_be_vec(), [1, 0]);
        assert_eq!(b.len(), 9);
        b.set_bit(8, false);
        assert_eq!(b.to_be_vec(), [0, 0]);
        assert_eq!(b.len(), 9);

        b = BooleanBitfield::new();
        b.set_bit(15, true);
        assert_eq!(b.to_be_vec(), [128, 0]);
        b.set_bit(15, false);
        assert_eq!(b.to_be_vec(), [0, 0]);
        assert_eq!(b.len(), 16);

        b = BooleanBitfield::new();
        b.set_bit(8, true);
        b.set_bit(15, true);
        assert_eq!(b.to_be_vec(), [129, 0]);
        b.set_bit(15, false);
        assert_eq!(b.to_be_vec(), [1, 0]);
        assert_eq!(b.len(), 16);
    }

    #[test]
    fn test_bitfield_get() {
        let test_nums = vec![0, 8, 15, 42, 1337];
        for i in test_nums {
            let mut b = BooleanBitfield::new();
            assert_eq!(b.get_bit(i), false);
            b.set_bit(i, true);
            assert_eq!(b.get_bit(i), true);
            b.set_bit(i, true);
        }
    }

    #[test]
    fn test_bitfield_num_true_bits() {
        let mut b = BooleanBitfield::new();
        assert_eq!(b.num_true_bits(), 0);
        b.set_bit(15, true);
        assert_eq!(b.num_true_bits(), 1);
        b.set_bit(15, false);
        assert_eq!(b.num_true_bits(), 0);
        b.set_bit(0, true);
        b.set_bit(7, true);
        b.set_bit(8, true);
        b.set_bit(1337, true);
        assert_eq!(b.num_true_bits(), 4);
    }
}
<commit_msg>Fix bug on BooleanBitfield decoding<commit_after>/*
 * Implemenation of a bitfield as a vec. Only
 * supports bytes (Vec<u8>) as the underlying
 * storage.
 *
 * A future implementation should be more efficient,
 * this is just to get the job done for now.
 */
extern crate ssz;

use std::cmp::max;

#[derive(Eq, Clone, Default, Debug)]
pub struct BooleanBitfield{
    len: usize,
    vec: Vec<u8>
}

impl BooleanBitfield {
    /// Create a new bitfield with a length of zero.
    pub fn new() -> Self {
        Self {
            len: 0,
            vec: vec![0]
        }
    }

    /// Create a new bitfield of a certain capacity
    pub fn with_capacity(capacity: usize) -> Self {
        let mut vec = Vec::with_capacity(capacity / 8 + 1);
        vec.push(0);
        Self {
            len: 0,
            vec
        }
    }

    /// Read the value of a bit.
    ///
    /// Will return `true` if the bit has been set to `true`
    /// without then being set to `False`.
    pub fn get_bit(&self, i: usize) -> bool {
        let bit = |i: usize| i % 8;
        let byte = |i: usize| i / 8;

        if byte(i) >= self.vec.len() {
            false
        } else {
             self.vec[byte(i)] & (1 << (bit(i) as u8)) != 0
        }
    }

    /// Set the value of a bit.
    ///
    /// If this bit is larger than the length of the underlying byte
    /// array it will be extended.
    pub fn set_bit(&mut self, i: usize, to: bool) {
        let bit = |i: usize| i % 8;
        let byte = |i: usize| i / 8;

        self.len = max(self.len, i + 1);

        if byte(i) >= self.vec.len() {
            self.vec.resize(byte(i) + 1, 0);
        }
        if to {
            self.vec[byte(i)] =
                self.vec[byte(i)] |  (1 << (bit(i) as u8))
        } else {
            self.vec[byte(i)] =
                self.vec[byte(i)] & !(1 << (bit(i) as u8))
        }
    }

    /// Return the "length" of this bitfield. Length is defined as
    /// the highest bit that has been set.
    ///
    /// Note: this is distinct from the length of the underlying
    /// vector.
    pub fn len(&self) -> usize { self.len }

    /// True if no bits have ever been set. A bit that is set and then
    /// unset will still count to the length of the bitfield.
    ///
    /// Note: this is distinct from the length of the underlying
    /// vector.
    pub fn is_empty(&self) -> bool { self.len == 0 }

    /// Iterate through the underlying vector and count the number of
    /// true bits.
    pub fn num_true_bits(&self) -> u64 {
        let mut count: u64 = 0;
        for byte in &self.vec {
            for bit in 0..8 {
                if byte & (1 << (bit as u8)) != 0 {
                    count += 1;
                }
            }
        }
        count
    }

    /// Iterate through the underlying vector and find the highest
    /// set bit. Useful for instantiating a new instance from
    /// some set of bytes.
    pub fn compute_length(bytes: &[u8]) -> usize {
        for byte in (0..bytes.len()).rev() {
            for bit in (0..8).rev() {
                if byte & (1 << (bit as u8)) != 0 {
                    return (byte * 8) + bit
                }
            }
        }
        0
    }

    /// Clone and return the underlying byte array (`Vec<u8>`).
    pub fn to_be_vec(&self) -> Vec<u8> {
        let mut o = self.vec.clone();
        o.reverse();
        o
    }
}

impl<'a> From<&'a [u8]> for BooleanBitfield {
    fn from(input: &[u8]) -> Self {
        let mut vec = input.to_vec();
        vec.reverse();
        BooleanBitfield {
            vec,
            len: BooleanBitfield::compute_length(input)
        }
    }
}

impl PartialEq for BooleanBitfield {
    fn eq(&self, other: &BooleanBitfield) -> bool {
        (self.vec == other.vec) &
            (self.len == other.len)
    }
}

impl ssz::Encodable for BooleanBitfield {
    fn ssz_append(&self, s: &mut ssz::SszStream) {
        s.append_vec(&self.to_be_vec());
    }
}

impl ssz::Decodable for BooleanBitfield {
    fn ssz_decode(bytes: &[u8], index: usize)
        -> Result<(Self, usize), ssz::DecodeError>
    {
        let len = ssz::decode::decode_length(
            bytes,
            index,
            ssz::LENGTH_BYTES)?;
        if (ssz::LENGTH_BYTES + len) > bytes.len() {
            return Err(ssz::DecodeError::TooShort);
        }
        if len == 0 {
            Ok((BooleanBitfield::new(),
                index + ssz::LENGTH_BYTES))
        } else {
            let b = BooleanBitfield::
                from(&bytes[(index + 4)..(index + len + 4)]);
            let index = index + ssz::LENGTH_BYTES + len;
            Ok((b, index))
        }
    }
}

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

    #[test]
    fn test_ssz_encoding() {
        let mut b = BooleanBitfield::new();
        b.set_bit(8, true);

        let mut stream = ssz::SszStream::new();
        stream.append(&b);

        assert_eq!(stream.drain(), vec![0, 0, 0, 2, 1, 0]);
    }

    #[test]
    fn test_ssz_decoding() {
        /*
         * Correct input
         */
        let input = vec![0, 0, 0, 2, 1, 0];
        let (b, i) = BooleanBitfield::ssz_decode(&input, 0).unwrap();
        assert_eq!(i, 6);
        assert_eq!(b.num_true_bits(), 1);
        assert_eq!(b.get_bit(8), true);

        /*
         * Input too long
         */
        let mut input = vec![0, 0, 0, 2, 1, 0];
        input.push(42);
        let (b, i) = BooleanBitfield::ssz_decode(&input, 0).unwrap();
        assert_eq!(i, 6);
        assert_eq!(b.num_true_bits(), 1);
        assert_eq!(b.get_bit(8), true);

        /*
         * Input too short
         */
        let input = vec![0, 0, 0, 2, 1];
        let res = BooleanBitfield::ssz_decode(&input, 0);
        assert_eq!(res, Err(ssz::DecodeError::TooShort));
    }

    #[test]
    fn test_new_bitfield_len() {
        let b = BooleanBitfield::new();
        assert_eq!(b.len(), 0);
        assert_eq!(b.to_be_vec(), vec![0]);

        let b = BooleanBitfield::with_capacity(100);
        assert_eq!(b.len(), 0);
        assert_eq!(b.to_be_vec(), vec![0]);
    }

    #[test]
    fn test_new_bitfield_len() {
        let b = BooleanBitfield::new();
        assert_eq!(b.len(), 0);
        assert_eq!(b.to_be_vec(), vec![0]);

        let b = BooleanBitfield::with_capacity(100);
        assert_eq!(b.len(), 0);
        assert_eq!(b.to_be_vec(), vec![0]);
    }

    #[test]
    fn test_bitfield_set() {
        let mut b = BooleanBitfield::new();
        b.set_bit(0, false);
        assert_eq!(b.to_be_vec(), [0]);

        b = BooleanBitfield::new();
        b.set_bit(7, true);
        assert_eq!(b.to_be_vec(), [128]);
        b.set_bit(7, false);
        assert_eq!(b.to_be_vec(), [0]);
        assert_eq!(b.len(), 8);

        b = BooleanBitfield::new();
        b.set_bit(7, true);
        b.set_bit(0, true);
        assert_eq!(b.to_be_vec(), [129]);
        b.set_bit(7, false);
        assert_eq!(b.to_be_vec(), [1]);
        assert_eq!(b.len(), 8);

        b = BooleanBitfield::new();
        b.set_bit(8, true);
        assert_eq!(b.to_be_vec(), [1, 0]);
        assert_eq!(b.len(), 9);
        b.set_bit(8, false);
        assert_eq!(b.to_be_vec(), [0, 0]);
        assert_eq!(b.len(), 9);

        b = BooleanBitfield::new();
        b.set_bit(15, true);
        assert_eq!(b.to_be_vec(), [128, 0]);
        b.set_bit(15, false);
        assert_eq!(b.to_be_vec(), [0, 0]);
        assert_eq!(b.len(), 16);

        b = BooleanBitfield::new();
        b.set_bit(8, true);
        b.set_bit(15, true);
        assert_eq!(b.to_be_vec(), [129, 0]);
        b.set_bit(15, false);
        assert_eq!(b.to_be_vec(), [1, 0]);
        assert_eq!(b.len(), 16);
    }

    #[test]
    fn test_bitfield_get() {
        let test_nums = vec![0, 8, 15, 42, 1337];
        for i in test_nums {
            let mut b = BooleanBitfield::new();
            assert_eq!(b.get_bit(i), false);
            b.set_bit(i, true);
            assert_eq!(b.get_bit(i), true);
            b.set_bit(i, true);
        }
    }

    #[test]
    fn test_bitfield_num_true_bits() {
        let mut b = BooleanBitfield::new();
        assert_eq!(b.num_true_bits(), 0);
        b.set_bit(15, true);
        assert_eq!(b.num_true_bits(), 1);
        b.set_bit(15, false);
        assert_eq!(b.num_true_bits(), 0);
        b.set_bit(0, true);
        b.set_bit(7, true);
        b.set_bit(8, true);
        b.set_bit(1337, true);
        assert_eq!(b.num_true_bits(), 4);
    }
}
<|endoftext|>


<commit_before>use std::num;

struct Vector {
    x:f32,
    y:f32,
    z:f32
}

impl Vector {
    fn new(x:f32,y:f32,z:f32) -> Vector {
        Vector { x:x, y:y, z:z }
    }
    fn scale(&self, s:f32)    -> Vector  { Vector { x:self.x*s, y:self.y*s, z:self.z*s } }
    fn plus(&self, b:Vector)  -> Vector  { Vector::new(self.x+b.x, self.y+b.y, self.z+b.z) }
    fn minus(&self, b:Vector) -> Vector  { Vector::new(self.x-b.x, self.y-b.y, self.z-b.z) }
    fn dot(&self, b:Vector)   -> f32     { self.x*b.x + self.y*b.y + self.z*b.z }
    fn magnitude(&self)       -> f32     { (self.dot(*self)).sqrt() }
    fn normalize(&self)       -> Vector  { self.scale(1.0/self.magnitude())  }
}

struct Ray {
    orig:Vector,
    dir:Vector,
}

struct Color {
    r:f32,
    g:f32,
    b:f32,
}

impl Color {
    fn scale (&self, s:f32) -> Color {
        Color { r: self.r*s, g:self.g*s, b:self.b*s }
    }
    fn plus (&self, b:Color) -> Color {
        Color { r: self.r + b.r, g: self.g + b.g, b: self.b + b.b }
    }
}

struct Sphere {
    center:Vector,
    radius:f32,
    color: Color,
}

impl Sphere {
    fn get_normal(&self, pt:Vector) -> Vector {
        return pt.minus(self.center).normalize();
    }
}

struct Light {
    position: Vector,
    color: Color,
}

static WHITE:Color  = Color { r:1.0, g:1.0, b:1.0};
static RED:Color    = Color { r:1.0, g:0.0, b:0.0};
static GREEN:Color  = Color { r:0.0, g:1.0, b:0.0};
static BLUE:Color   = Color { r:0.0, g:0.0, b:1.0};

static LIGHT1:Light = Light {
    position: Vector { x: 0.7, y: -1.0, z: 1.7} ,
    color: WHITE
};

fn main() {
    println!("Hello, worlds!");
    let lut = vec!(".","-","+","*","X","M");

    let w = 20*4i;
    let h = 10*4i;

    let scene = vec!(
        Sphere{ center: Vector::new(-1.0, 0.0, 3.0), radius: 0.3, color: RED },
        Sphere{ center: Vector::new( 0.0, 0.0, 3.0), radius: 0.8, color: GREEN },
        Sphere{ center: Vector::new( 1.0, 0.0, 3.0), radius: 0.3, color: BLUE }
        );

    for j in range(0,h) {
        println!("--");
        for i in range(0,w) {
            //let tMax = 10000f32;
            let fw:f32 = w as f32;
            let fi:f32 = i as f32;
            let fj:f32 = j as f32;
            let fh:f32 = h as f32;

            let ray = Ray {
                orig: Vector::new(0.0,0.0,0.0),
                dir:  Vector::new((fi-fw/2.0)/fw, (fj-fh/2.0)/fh,1.0).normalize(),
            };

            let mut objHitObj:Option<(Sphere,f32)> = None;

            for obj in scene.iter() {
                let ret = intersect_sphere(ray, obj.center, obj.radius);
                if ret.hit {
                    objHitObj = Some((*obj,ret.tval));
                }
            }
            let pixel = match objHitObj {
                Some((obj,tval)) => lut[shade_pixel(ray,obj,tval)],
                None             => " "
            };

            print!("{}",pixel);
        }
    }

    println!("we are done!");
}

fn shade_pixel(ray:Ray, obj:Sphere, tval:f32) -> uint {
    let pi = ray.orig.plus(ray.dir.scale(tval));
    let color = diffuse_shading(pi, obj, LIGHT1);
    let col = (color.r + color.g + color.b) / 3.0;
    (col * 6.0) as uint
}

struct HitPoint {
    hit:bool,
    tval:f32,
}

fn intersect_sphere(ray:Ray, center:Vector, radius:f32) -> HitPoint {
    let l = center.minus(ray.orig);
    let tca = l.dot(ray.dir);
    if tca < 0.0 {
        return HitPoint { hit:false, tval:-1.0 };
    }
    let d2 = l.dot(l) - tca*tca;
    let r2 = radius*radius;
    if d2 > r2 {
        return HitPoint { hit: false, tval:-1.0 };
    }
    let thc = (r2-d2).sqrt();
    let t0 = tca-thc;
    //let t1 = tca+thc;
    if t0 > 10000.0 {
        return HitPoint { hit: false, tval: -1.0 };
    }
    return HitPoint { hit: true, tval: t0}

}

fn clamp(x:f32,a:f32,b:f32) -> f32{
    if x < a { return a;  }
    if x > b { return b; }
    return x;
}

fn diffuse_shading(pi:Vector, obj:Sphere, light:Light) -> Color{
    let n = obj.get_normal(pi);
    let lam1 = light.position.minus(pi).normalize().dot(n);
    let lam2 = clamp(lam1,0.0,1.0);
    light.color.scale(lam2*0.5).plus(obj.color.scale(0.3))
}
<commit_msg>For some reason the Rust version I'm using (0.13.0-nightly) didn't like the static WHITE in static LIGHT1.<commit_after>use std::num;

struct Vector {
    x:f32,
    y:f32,
    z:f32
}

impl Vector {
    fn new(x:f32,y:f32,z:f32) -> Vector {
        Vector { x:x, y:y, z:z }
    }
    fn scale(&self, s:f32)    -> Vector  { Vector { x:self.x*s, y:self.y*s, z:self.z*s } }
    fn plus(&self, b:Vector)  -> Vector  { Vector::new(self.x+b.x, self.y+b.y, self.z+b.z) }
    fn minus(&self, b:Vector) -> Vector  { Vector::new(self.x-b.x, self.y-b.y, self.z-b.z) }
    fn dot(&self, b:Vector)   -> f32     { self.x*b.x + self.y*b.y + self.z*b.z }
    fn magnitude(&self)       -> f32     { (self.dot(*self)).sqrt() }
    fn normalize(&self)       -> Vector  { self.scale(1.0/self.magnitude())  }
}

struct Ray {
    orig:Vector,
    dir:Vector,
}

struct Color {
    r:f32,
    g:f32,
    b:f32,
}

impl Color {
    fn scale (&self, s:f32) -> Color {
        Color { r: self.r*s, g:self.g*s, b:self.b*s }
    }
    fn plus (&self, b:Color) -> Color {
        Color { r: self.r + b.r, g: self.g + b.g, b: self.b + b.b }
    }
}

struct Sphere {
    center:Vector,
    radius:f32,
    color: Color,
}

impl Sphere {
    fn get_normal(&self, pt:Vector) -> Vector {
        return pt.minus(self.center).normalize();
    }
}

struct Light {
    position: Vector,
    color: Color,
}

static WHITE:Color  = Color { r:1.0, g:1.0, b:1.0};
static RED:Color    = Color { r:1.0, g:0.0, b:0.0};
static GREEN:Color  = Color { r:0.0, g:1.0, b:0.0};
static BLUE:Color   = Color { r:0.0, g:0.0, b:1.0};

static LIGHT1:Light = Light {
    position: Vector { x: 0.7, y: -1.0, z: 1.7} ,
    color: Color { r:1.0, g:1.0, b:1.0} ,
};

fn main() {
    println!("Hello, worlds!");
    let lut = vec!(".","-","+","*","X","M");

    let w = 20*4i;
    let h = 10*4i;

    let scene = vec!(
        Sphere{ center: Vector::new(-1.0, 0.0, 3.0), radius: 0.3, color: RED },
        Sphere{ center: Vector::new( 0.0, 0.0, 3.0), radius: 0.8, color: GREEN },
        Sphere{ center: Vector::new( 1.0, 0.0, 3.0), radius: 0.3, color: BLUE }
        );

    for j in range(0,h) {
        println!("--");
        for i in range(0,w) {
            //let tMax = 10000f32;
            let fw:f32 = w as f32;
            let fi:f32 = i as f32;
            let fj:f32 = j as f32;
            let fh:f32 = h as f32;

            let ray = Ray {
                orig: Vector::new(0.0,0.0,0.0),
                dir:  Vector::new((fi-fw/2.0)/fw, (fj-fh/2.0)/fh,1.0).normalize(),
            };

            let mut objHitObj:Option<(Sphere,f32)> = None;

            for obj in scene.iter() {
                let ret = intersect_sphere(ray, obj.center, obj.radius);
                if ret.hit {
                    objHitObj = Some((*obj,ret.tval));
                }
            }
            let pixel = match objHitObj {
                Some((obj,tval)) => lut[shade_pixel(ray,obj,tval)],
                None             => " "
            };

            print!("{}",pixel);
        }
    }

    println!("we are done!");
}

fn shade_pixel(ray:Ray, obj:Sphere, tval:f32) -> uint {
    let pi = ray.orig.plus(ray.dir.scale(tval));
    let color = diffuse_shading(pi, obj, LIGHT1);
    let col = (color.r + color.g + color.b) / 3.0;
    (col * 6.0) as uint
}

struct HitPoint {
    hit:bool,
    tval:f32,
}

fn intersect_sphere(ray:Ray, center:Vector, radius:f32) -> HitPoint {
    let l = center.minus(ray.orig);
    let tca = l.dot(ray.dir);
    if tca < 0.0 {
        return HitPoint { hit:false, tval:-1.0 };
    }
    let d2 = l.dot(l) - tca*tca;
    let r2 = radius*radius;
    if d2 > r2 {
        return HitPoint { hit: false, tval:-1.0 };
    }
    let thc = (r2-d2).sqrt();
    let t0 = tca-thc;
    //let t1 = tca+thc;
    if t0 > 10000.0 {
        return HitPoint { hit: false, tval: -1.0 };
    }
    return HitPoint { hit: true, tval: t0}

}

fn clamp(x:f32,a:f32,b:f32) -> f32{
    if x < a { return a;  }
    if x > b { return b; }
    return x;
}

fn diffuse_shading(pi:Vector, obj:Sphere, light:Light) -> Color{
    let n = obj.get_normal(pi);
    let lam1 = light.position.minus(pi).normalize().dot(n);
    let lam2 = clamp(lam1,0.0,1.0);
    light.color.scale(lam2*0.5).plus(obj.color.scale(0.3))
}
<|endoftext|>


<commit_before>extern crate pleco;
extern crate chrono;
extern crate pleco_engine;

use pleco::bot_prelude::{JamboreeSearcher,Searcher,IterativeSearcher};
use pleco::{Board,BitMove};
use pleco::engine::UCILimit;

use pleco_engine::pleco_searcher::PlecoSearcher;

use std::thread;
use chrono::*;

fn main() {
    run_many();
}

fn run_one() {
    let mut s = PlecoSearcher::init(true);
    let mut board = Board::default();

    let mut local: Duration = Duration::seconds(2);

    let mut i = 0;

    while i < 700 && !board.checkmate() && !board.stalemate() {
        board.pretty_print();

        if i % 2 == 1 {
            local = Duration::span(|| {
                let mov = JamboreeSearcher::best_move_depth(board.shallow_clone(), 5);
                println!("Jamboree searcher: {}", mov);
                board.apply_move(mov);
            });
        } else {
            s.search(&board, &UCILimit::Infinite);
            thread::sleep_ms(local.num_milliseconds() as u32);
            let mov = s.stop_search();
            println!("Pleco searcher: {}",mov);
            board.apply_move(mov);
        }
        i += 1;
    }

    println!("i = {}",i);
    board.pretty_print();
    if i % 2 == 1 {
        println!("Pleco Wins");
    } else {
        println!("Jamboree wins!");
    }

}

fn run_many() {
    let mut j = 100;
    let mut wins = 0;
    let mut loses = 0;
    let mut draws = 0;
    while j > 0 {
        let mut s = PlecoSearcher::init(false);
        s.clear_tt();
        let mut board = Board::default();

        let mut local: Duration = Duration::seconds(1);

        let max_moves = 250;
        let mut i = max_moves;

        while i > 0 && !board.checkmate() && !board.stalemate() {
            if i % 2 == 1 {
                local = Duration::span(|| {
                    let mov = if i < max_moves - 70 {
                        JamboreeSearcher::best_move_depth(board.shallow_clone(), 5)
                    } else {
                        JamboreeSearcher::best_move_depth(board.shallow_clone(), 4)
                    };
                    board.apply_move(mov);
                });
                local = local.max(Duration::milliseconds(1));
            } else {
                s.search(&board, &UCILimit::Infinite);
                thread::sleep(local.to_std().unwrap());
                let mov = s.stop_search();
                board.apply_move(mov);
            }
            i -= 1;
        }

        board.pretty_print();
        if board.stalemate() || i == 0 || !board.in_check() {
            print!("Draw! rem: {} ", i % 2);
            if i == 0 {
                println!("i == 0");
            } else if board.stalemate() {
                println!("Stalemate");
            } else {
                println!("Not in check");
            }
            draws += 1;
        } else if i % 2 == 1 {
            println!("Pleco Wins");
            wins += 1;
        } else {
            println!("Jamboree wins!");
            loses += 1;
        }
        j -= 1;
        println!("rounds = {}, Hash {}",max_moves - i,s.hash_percent());

        if j % 5 == 3 {
            println!("W/L/D {}-{}-{}",wins,loses,draws);
        }
    }
    println!("W/L/D {}-{}-{}",wins,loses,draws);

}<commit_msg>Slight modifications to main<commit_after>extern crate pleco;
extern crate chrono;
extern crate pleco_engine;

use pleco::bot_prelude::{JamboreeSearcher,Searcher,IterativeSearcher};
use pleco::{Board,BitMove};
use pleco::engine::UCILimit;

use pleco_engine::pleco_searcher::PlecoSearcher;

use std::thread;
use chrono::*;

fn main() {
    run_many();
}

fn run_one() {
    let mut s = PlecoSearcher::init(true);
    let mut board = Board::default();

    let mut local: Duration = Duration::seconds(2);

    let mut i = 0;

    while i < 700 && !board.checkmate() && !board.stalemate() {
        board.pretty_print();

        if i % 2 == 1 {
            local = Duration::span(|| {
                let mov = JamboreeSearcher::best_move_depth(board.shallow_clone(), 5);
                println!("Jamboree searcher: {}", mov);
                board.apply_move(mov);
            });
        } else {
            s.search(&board, &UCILimit::Infinite);
            thread::sleep_ms(local.num_milliseconds() as u32);
            let mov = s.stop_search();
            println!("Pleco searcher: {}",mov);
            board.apply_move(mov);
        }
        i += 1;
    }

    println!("i = {}",i);
    board.pretty_print();
    if i % 2 == 1 {
        println!("Pleco Wins");
    } else {
        println!("Jamboree wins!");
    }

}

fn run_many() {
    let mut j = 1000;
    let mut wins = 0;
    let mut loses = 0;
    let mut draws = 0;
    while j > 0 {
        let mut s = PlecoSearcher::init(false);
        s.clear_tt();
        let mut board = Board::default();

        let mut local: Duration = Duration::seconds(1);

        let max_moves = 250;
        let mut i = max_moves;

        while i > 0 && !board.checkmate() && !board.stalemate() {
            if i % 2 == 1 {
                local = Duration::span(|| {
                    let mov = if i < max_moves - 40 {
                        JamboreeSearcher::best_move_depth(board.shallow_clone(), 5)
                    } else {
                        JamboreeSearcher::best_move_depth(board.shallow_clone(), 4)
                    };
                    board.apply_move(mov);
                });
                local = local.max(Duration::milliseconds(1));
            } else {
                s.search(&board, &UCILimit::Infinite);
                thread::sleep(local.to_std().unwrap());
                let mov = s.stop_search();
                board.apply_move(mov);
            }
            i -= 1;
        }

        board.pretty_print();
        if board.stalemate() || i == 0 || !board.in_check() {
            print!("Draw! rem: {} ", i % 2);
            if i == 0 {
                println!("i == 0");
            } else if board.stalemate() {
                println!("Stalemate");
            } else {
                println!("Not in check");
            }
            draws += 1;
        } else if i % 2 == 1 {
            println!("Pleco Wins");
            wins += 1;
        } else {
            println!("Jamboree wins!");
            loses += 1;
        }
        j -= 1;
        println!("rounds = {}, Hash {}",max_moves - i,s.hash_percent());

        if j % 6 == 3 {
            println!("W/L/D {}-{}-{}",wins,loses,draws);
        }
    }
    println!("W/L/D {}-{}-{}",wins,loses,draws);

}<|endoftext|>