<commit_before>use std::ops::Deref;

use config::gobjects::GObject;
use env::Env;
use library;
use nameutil::*;
use super::*;
use super::child_properties::ChildProperties;
use super::imports::Imports;
use super::info_base::InfoBase;
use super::signatures::Signatures;
use traits::*;
use super::functions::Visibility;

#[derive(Debug, Default)]
pub struct Info {
    pub base: InfoBase,
    pub c_type: String,
    pub c_class_type: Option<String>,
    pub get_type: String,
    pub supertypes: Vec<general::StatusedTypeId>,
    pub generate_trait: bool,
    pub trait_name: String,
    pub has_constructors: bool,
    pub has_methods: bool,
    pub has_functions: bool,
    pub signals: Vec<signals::Info>,
    pub notify_signals: Vec<signals::Info>,
    pub trampolines: trampolines::Trampolines,
    pub properties: Vec<properties::Property>,
    pub child_properties: ChildProperties,
    pub signatures: Signatures,
}

impl Info {
    pub fn has_signals(&self) -> bool {
        self.signals.iter().any(|s| s.trampoline_name.is_ok())
            || self.notify_signals
                .iter()
                .any(|s| s.trampoline_name.is_ok())
    }

    pub fn has_action_signals(&self) -> bool {
        self.signals.iter().any(|s| s.action_emit_name.is_some())
    }
}

impl Deref for Info {
    type Target = InfoBase;

    fn deref(&self) -> &InfoBase {
        &self.base
    }
}

pub fn has_known_subtypes(env: &Env, class_tid: library::TypeId) -> bool {
    for child_tid in env.class_hierarchy.subtypes(class_tid) {
        let child_name = child_tid.full_name(&env.library);
        let status = env.config
            .objects
            .get(&child_name)
            .map(|o| o.status)
            .unwrap_or_default();
        if status.normal() {
            return true;
        }
    }

    false
}

pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info> {
    info!("Analyzing class {}", obj.name);
    let full_name = obj.name.clone();

    let class_tid = match env.library.find_type(0, &full_name) {
        Some(tid) => tid,
        None => return None,
    };

    let type_ = env.type_(class_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let klass: &library::Class = match type_.maybe_ref() {
        Some(klass) => klass,
        None => return None,
    };

    let mut imports = Imports::with_defined(&env.library, &name);
    imports.add("glib::translate::*", None);
    imports.add("ffi", None);
    imports.add("glib_ffi", None);
    imports.add("gobject_ffi", None);
    imports.add("std::mem", None);
    imports.add("std::ptr", None);
    if obj.generate_display_trait {
        imports.add("std::fmt", None);
    }

    let supertypes = supertypes::analyze(env, class_tid, &mut imports);

    let mut generate_trait = obj.generate_trait;
    let trait_name = obj.trait_name
        .as_ref()
        .cloned()
        .unwrap_or_else(|| format!("{}Ext", name));

    // Sanity check the user's configuration. It's unlikely that not generating
    // a trait is wanted if there are subtypes in this very crate
    if !generate_trait && has_known_subtypes(env, class_tid) {
        error!(
            "Not generating trait for {} although subtypes exist",
            full_name
        );
    }

    let mut trampolines =
        trampolines::Trampolines::with_capacity(klass.signals.len() + klass.properties.len());
    let mut signatures = Signatures::with_capacity(klass.functions.len());

    let mut functions = functions::analyze(
        env,
        &klass.functions,
        class_tid,
        generate_trait,
        obj,
        &mut imports,
        Some(&mut signatures),
        Some(deps),
    );
    let mut specials = special_functions::extract(&mut functions);
    // `copy` will duplicate an object while `clone` just adds a reference
    special_functions::unhide(&mut functions, &specials, special_functions::Type::Copy);
    // these are all automatically derived on objects and compare by pointer. If such functions
    // exist they will provide additional functionality
    for t in &[special_functions::Type::Hash,
              special_functions::Type::Equal,
              special_functions::Type::Compare,
              special_functions::Type::ToString,
    ] {
        special_functions::unhide(&mut functions, &specials, *t);
        specials.remove(t);
    }
    special_functions::analyze_imports(&specials, &mut imports);

    let signals = signals::analyze(
        env,
        &klass.signals,
        class_tid,
        generate_trait,
        &mut trampolines,
        obj,
        &mut imports,
    );
    let (properties, notify_signals) = properties::analyze(
        env,
        &klass.properties,
        class_tid,
        generate_trait,
        &mut trampolines,
        obj,
        &mut imports,
        &signatures,
        deps,
    );

    let (version, deprecated_version) = info_base::versions(
        env,
        obj,
        &functions,
        klass.version,
        klass.deprecated_version,
    );

    let child_properties =
        child_properties::analyze(env, obj.child_properties.as_ref(), class_tid, &mut imports);

    let has_methods = functions
        .iter()
        .any(|f| f.kind == library::FunctionKind::Method);
    let has_signals = signals.iter().any(|s| s.trampoline_name.is_ok())
        || notify_signals.iter().any(|s| s.trampoline_name.is_ok());

    // There's no point in generating a trait if there are no signals, methods, properties
    // and child properties: it would be empty
    if generate_trait && !has_signals && !has_methods && properties.is_empty()
        && child_properties.is_empty()
    {
        generate_trait = false;
    }

    let has_async = functions
        .iter()
        .any(|f| f.async && f.visibility == Visibility::Public);

    if generate_trait && (!properties.is_empty() || has_signals || has_async) {
        imports.add("glib", None);
    }
    if generate_trait
        && (has_methods || !properties.is_empty() || !child_properties.is_empty() || has_signals)
    {
        imports.add("glib::object::IsA", None);
    }

    if obj.concurrency == library::Concurrency::SendUnique {
        imports.add("glib::ObjectExt", None);
    }

    let base = InfoBase {
        full_name,
        type_id: class_tid,
        name,
        functions,
        specials,
        imports,
        version,
        deprecated_version,
        cfg_condition: obj.cfg_condition.clone(),
        concurrency: obj.concurrency,
    };

    // patch up trait methods in the symbol table
    if generate_trait {
        let mut symbols = env.symbols.borrow_mut();
        for func in base.methods() {
            if let Some(symbol) = symbols.by_c_name_mut(&func.glib_name) {
                symbol.make_trait_method(&trait_name);
            }
        }
    }

    let has_constructors = !base.constructors().is_empty();
    let has_functions = !base.functions().is_empty();

    let info = Info {
        base,
        c_type: klass.c_type.clone(),
        c_class_type: klass.c_class_type.clone(),
        get_type: klass.glib_get_type.clone(),
        supertypes,
        generate_trait,
        trait_name,
        has_constructors,
        has_methods,
        has_functions,
        signals,
        notify_signals,
        trampolines,
        properties,
        child_properties,
        signatures,
    };

    Some(info)
}

pub fn interface(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info> {
    info!("Analyzing interface {}", obj.name);
    let full_name = obj.name.clone();

    let iface_tid = match env.library.find_type(0, &full_name) {
        Some(tid) => tid,
        None => return None,
    };

    let type_ = env.type_(iface_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let iface: &library::Interface = match type_.maybe_ref() {
        Some(iface) => iface,
        None => return None,
    };

    let mut imports = Imports::with_defined(&env.library, &name);
    imports.add("glib::translate::*", None);
    imports.add("ffi", None);
    imports.add("glib::object::IsA", None);
    imports.add("glib_ffi", None);
    imports.add("gobject_ffi", None);
    imports.add("std::mem", None);
    imports.add("std::ptr", None);

    let supertypes = supertypes::analyze(env, iface_tid, &mut imports);

    let trait_name = obj.trait_name
        .as_ref()
        .cloned()
        .unwrap_or_else(|| format!("{}Ext", name));

    let mut trampolines = trampolines::Trampolines::with_capacity(iface.signals.len());
    let mut signatures = Signatures::with_capacity(iface.functions.len());

    let functions = functions::analyze(
        env,
        &iface.functions,
        iface_tid,
        true,
        obj,
        &mut imports,
        Some(&mut signatures),
        Some(deps),
    );

    let signals = signals::analyze(
        env,
        &iface.signals,
        iface_tid,
        true,
        &mut trampolines,
        obj,
        &mut imports,
    );
    let (properties, notify_signals) = properties::analyze(
        env,
        &iface.properties,
        iface_tid,
        true,
        &mut trampolines,
        obj,
        &mut imports,
        &signatures,
        deps,
    );

    let (version, deprecated_version) = info_base::versions(
        env,
        obj,
        &functions,
        iface.version,
        iface.deprecated_version,
    );

    if !properties.is_empty() {
        imports.add("glib", None);
    }

    if obj.concurrency == library::Concurrency::SendUnique {
        imports.add("glib::ObjectExt", None);
    }

    let base = InfoBase {
        full_name,
        type_id: iface_tid,
        name,
        functions,
        specials: Default::default(),
        imports,
        version,
        deprecated_version,
        cfg_condition: obj.cfg_condition.clone(),
        concurrency: obj.concurrency,
    };

    let has_methods = !base.methods().is_empty();
    let has_functions = !base.functions().is_empty();

    let info = Info {
        base,
        c_type: iface.c_type.clone(),
        c_class_type: iface.c_class_type.clone(),
        get_type: iface.glib_get_type.clone(),
        supertypes,
        generate_trait: true,
        trait_name,
        has_methods,
        has_functions,
        signals,
        notify_signals,
        trampolines,
        properties,
        signatures,
        ..Default::default()
    };

    Some(info)
}
<commit_msg>Fix display generation<commit_after>use std::ops::Deref;

use config::gobjects::GObject;
use env::Env;
use library;
use nameutil::*;
use super::*;
use super::child_properties::ChildProperties;
use super::imports::Imports;
use super::info_base::InfoBase;
use super::signatures::Signatures;
use traits::*;
use super::functions::Visibility;

#[derive(Debug, Default)]
pub struct Info {
    pub base: InfoBase,
    pub c_type: String,
    pub c_class_type: Option<String>,
    pub get_type: String,
    pub supertypes: Vec<general::StatusedTypeId>,
    pub generate_trait: bool,
    pub trait_name: String,
    pub has_constructors: bool,
    pub has_methods: bool,
    pub has_functions: bool,
    pub signals: Vec<signals::Info>,
    pub notify_signals: Vec<signals::Info>,
    pub trampolines: trampolines::Trampolines,
    pub properties: Vec<properties::Property>,
    pub child_properties: ChildProperties,
    pub signatures: Signatures,
}

impl Info {
    pub fn has_signals(&self) -> bool {
        self.signals.iter().any(|s| s.trampoline_name.is_ok())
            || self.notify_signals
                .iter()
                .any(|s| s.trampoline_name.is_ok())
    }

    pub fn has_action_signals(&self) -> bool {
        self.signals.iter().any(|s| s.action_emit_name.is_some())
    }
}

impl Deref for Info {
    type Target = InfoBase;

    fn deref(&self) -> &InfoBase {
        &self.base
    }
}

pub fn has_known_subtypes(env: &Env, class_tid: library::TypeId) -> bool {
    for child_tid in env.class_hierarchy.subtypes(class_tid) {
        let child_name = child_tid.full_name(&env.library);
        let status = env.config
            .objects
            .get(&child_name)
            .map(|o| o.status)
            .unwrap_or_default();
        if status.normal() {
            return true;
        }
    }

    false
}

pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info> {
    info!("Analyzing class {}", obj.name);
    let full_name = obj.name.clone();

    let class_tid = match env.library.find_type(0, &full_name) {
        Some(tid) => tid,
        None => return None,
    };

    let type_ = env.type_(class_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let klass: &library::Class = match type_.maybe_ref() {
        Some(klass) => klass,
        None => return None,
    };

    let mut imports = Imports::with_defined(&env.library, &name);
    imports.add("glib::translate::*", None);
    imports.add("ffi", None);
    imports.add("glib_ffi", None);
    imports.add("gobject_ffi", None);
    imports.add("std::mem", None);
    imports.add("std::ptr", None);
    if obj.generate_display_trait {
        imports.add("std::fmt", None);
    }

    let supertypes = supertypes::analyze(env, class_tid, &mut imports);

    let mut generate_trait = obj.generate_trait;
    let trait_name = obj.trait_name
        .as_ref()
        .cloned()
        .unwrap_or_else(|| format!("{}Ext", name));

    // Sanity check the user's configuration. It's unlikely that not generating
    // a trait is wanted if there are subtypes in this very crate
    if !generate_trait && has_known_subtypes(env, class_tid) {
        error!(
            "Not generating trait for {} although subtypes exist",
            full_name
        );
    }

    let mut trampolines =
        trampolines::Trampolines::with_capacity(klass.signals.len() + klass.properties.len());
    let mut signatures = Signatures::with_capacity(klass.functions.len());

    let mut functions = functions::analyze(
        env,
        &klass.functions,
        class_tid,
        generate_trait,
        obj,
        &mut imports,
        Some(&mut signatures),
        Some(deps),
    );
    let mut specials = special_functions::extract(&mut functions);
    // `copy` will duplicate an object while `clone` just adds a reference
    special_functions::unhide(&mut functions, &specials, special_functions::Type::Copy);
    // these are all automatically derived on objects and compare by pointer. If such functions
    // exist they will provide additional functionality
    for t in &[special_functions::Type::Hash,
              special_functions::Type::Equal,
              special_functions::Type::Compare,
              special_functions::Type::ToString,
    ] {
        special_functions::unhide(&mut functions, &specials, *t);
        specials.remove(t);
    }
    special_functions::analyze_imports(&specials, &mut imports);

    let signals = signals::analyze(
        env,
        &klass.signals,
        class_tid,
        generate_trait,
        &mut trampolines,
        obj,
        &mut imports,
    );
    let (properties, notify_signals) = properties::analyze(
        env,
        &klass.properties,
        class_tid,
        generate_trait,
        &mut trampolines,
        obj,
        &mut imports,
        &signatures,
        deps,
    );

    let (version, deprecated_version) = info_base::versions(
        env,
        obj,
        &functions,
        klass.version,
        klass.deprecated_version,
    );

    let child_properties =
        child_properties::analyze(env, obj.child_properties.as_ref(), class_tid, &mut imports);

    let has_methods = functions
        .iter()
        .any(|f| f.kind == library::FunctionKind::Method);
    let has_signals = signals.iter().any(|s| s.trampoline_name.is_ok())
        || notify_signals.iter().any(|s| s.trampoline_name.is_ok());

    // There's no point in generating a trait if there are no signals, methods, properties
    // and child properties: it would be empty
    if generate_trait && !has_signals && !has_methods && properties.is_empty()
        && child_properties.is_empty()
    {
        generate_trait = false;
    }

    let has_async = functions
        .iter()
        .any(|f| f.async && f.visibility == Visibility::Public);

    if generate_trait && (!properties.is_empty() || has_signals || has_async) {
        imports.add("glib", None);
    }
    if generate_trait
        && (has_methods || !properties.is_empty() || !child_properties.is_empty() || has_signals)
    {
        imports.add("glib::object::IsA", None);
    }

    if obj.concurrency == library::Concurrency::SendUnique {
        imports.add("glib::ObjectExt", None);
    }

    let base = InfoBase {
        full_name,
        type_id: class_tid,
        name,
        functions,
        specials,
        imports,
        version,
        deprecated_version,
        cfg_condition: obj.cfg_condition.clone(),
        concurrency: obj.concurrency,
    };

    // patch up trait methods in the symbol table
    if generate_trait {
        let mut symbols = env.symbols.borrow_mut();
        for func in base.methods() {
            if let Some(symbol) = symbols.by_c_name_mut(&func.glib_name) {
                symbol.make_trait_method(&trait_name);
            }
        }
    }

    let has_constructors = !base.constructors().is_empty();
    let has_functions = !base.functions().is_empty();

    let info = Info {
        base,
        c_type: klass.c_type.clone(),
        c_class_type: klass.c_class_type.clone(),
        get_type: klass.glib_get_type.clone(),
        supertypes,
        generate_trait,
        trait_name,
        has_constructors,
        has_methods,
        has_functions,
        signals,
        notify_signals,
        trampolines,
        properties,
        child_properties,
        signatures,
    };

    Some(info)
}

pub fn interface(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info> {
    info!("Analyzing interface {}", obj.name);
    let full_name = obj.name.clone();

    let iface_tid = match env.library.find_type(0, &full_name) {
        Some(tid) => tid,
        None => return None,
    };

    let type_ = env.type_(iface_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let iface: &library::Interface = match type_.maybe_ref() {
        Some(iface) => iface,
        None => return None,
    };

    let mut imports = Imports::with_defined(&env.library, &name);
    imports.add("glib::translate::*", None);
    imports.add("ffi", None);
    imports.add("glib::object::IsA", None);
    imports.add("glib_ffi", None);
    imports.add("gobject_ffi", None);
    imports.add("std::mem", None);
    imports.add("std::ptr", None);
    if obj.generate_display_trait {
        imports.add("std::fmt", None);
    }

    let supertypes = supertypes::analyze(env, iface_tid, &mut imports);

    let trait_name = obj.trait_name
        .as_ref()
        .cloned()
        .unwrap_or_else(|| format!("{}Ext", name));

    let mut trampolines = trampolines::Trampolines::with_capacity(iface.signals.len());
    let mut signatures = Signatures::with_capacity(iface.functions.len());

    let functions = functions::analyze(
        env,
        &iface.functions,
        iface_tid,
        true,
        obj,
        &mut imports,
        Some(&mut signatures),
        Some(deps),
    );

    let signals = signals::analyze(
        env,
        &iface.signals,
        iface_tid,
        true,
        &mut trampolines,
        obj,
        &mut imports,
    );
    let (properties, notify_signals) = properties::analyze(
        env,
        &iface.properties,
        iface_tid,
        true,
        &mut trampolines,
        obj,
        &mut imports,
        &signatures,
        deps,
    );

    let (version, deprecated_version) = info_base::versions(
        env,
        obj,
        &functions,
        iface.version,
        iface.deprecated_version,
    );

    if !properties.is_empty() {
        imports.add("glib", None);
    }

    if obj.concurrency == library::Concurrency::SendUnique {
        imports.add("glib::ObjectExt", None);
    }

    let base = InfoBase {
        full_name,
        type_id: iface_tid,
        name,
        functions,
        specials: Default::default(),
        imports,
        version,
        deprecated_version,
        cfg_condition: obj.cfg_condition.clone(),
        concurrency: obj.concurrency,
    };

    let has_methods = !base.methods().is_empty();
    let has_functions = !base.functions().is_empty();

    let info = Info {
        base,
        c_type: iface.c_type.clone(),
        c_class_type: iface.c_class_type.clone(),
        get_type: iface.glib_get_type.clone(),
        supertypes,
        generate_trait: true,
        trait_name,
        has_methods,
        has_functions,
        signals,
        notify_signals,
        trampolines,
        properties,
        signatures,
        ..Default::default()
    };

    Some(info)
}
<|endoftext|>


<commit_before>//! TODO Fill in

use std::fmt::{self, Display, Formatter};
use std::default::Default;
use std::rc::Rc;
use rlua::{self, Table, Lua, UserData, ToLua, Value};
use super::object::{Object, Objectable};
use super::class::{self, Class, ClassBuilder};

#[derive(Clone, Debug)]
pub struct AwesomeState {
    // TODO Fill in
    dummy: i32
}

pub struct Awesome<'lua>(Table<'lua>);

impl Default for AwesomeState {
    fn default() -> Self {
        AwesomeState {
            dummy: 0
        }
    }
}

impl <'lua> Awesome<'lua> {
    fn new(lua: &Lua) -> rlua::Result<Object> {
        // TODO FIXME
        let class = class::button_class(lua)?;
        Ok(Awesome::allocate(lua, class)?.build())
    }
}

impl Display for AwesomeState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Awesome: {:p}", self)
    }
}

impl <'lua> ToLua<'lua> for Awesome<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.0.to_lua(lua)
    }
}

impl UserData for AwesomeState {}

pub fn init(lua: &Lua) -> rlua::Result<Class> {
    property_setup(lua, method_setup(lua, Class::builder(lua, Some(Rc::new(Awesome::new)), None, None)?)?)?
        .save_class("awesome")?
        .build()
}

fn method_setup<'lua>(lua: &'lua Lua, builder: ClassBuilder<'lua>) -> rlua::Result<ClassBuilder<'lua>> {
    // TODO Do properly
    use super::dummy;
    builder.method("connect_signal".into(), lua.create_function(dummy))?
           .method("register_xproperty".into(), lua.create_function(dummy))?
           .method("xkb_get_group_names".into(), lua.create_function(dummy))?
           .method("restart".into(), lua.create_function(dummy))?
           .method("load_image".into(), lua.create_function(dummy))?
           .method("quit".into(), lua.create_function(dummy))
}

fn property_setup<'lua>(lua: &'lua Lua, builder: ClassBuilder<'lua>) -> rlua::Result<ClassBuilder<'lua>> {
    // TODO Do properly
    builder.dummy_property("version".into(), "0".to_lua(lua)?)?
           .dummy_property("themes_path".into(), "/usr/share/awesome/themes".to_lua(lua)?)?
           .dummy_property("conffile".into(), "".to_lua(lua)?)
}

impl_objectable!(Awesome, AwesomeState);
<commit_msg>Added register_xproperty, xkb_get_group_names, restart<commit_after>//! TODO Fill in

use std::fmt::{self, Display, Formatter};
use std::default::Default;
use std::rc::Rc;
use rlua::{self, Table, Lua, UserData, ToLua, Value};
use super::object::{Object, Objectable};
use super::class::{self, Class, ClassBuilder};

#[derive(Clone, Debug)]
pub struct AwesomeState {
    // TODO Fill in
    dummy: i32
}

pub struct Awesome<'lua>(Table<'lua>);

impl Default for AwesomeState {
    fn default() -> Self {
        AwesomeState {
            dummy: 0
        }
    }
}

impl <'lua> Awesome<'lua> {
    fn new(lua: &Lua) -> rlua::Result<Object> {
        // TODO FIXME
        let class = class::button_class(lua)?;
        Ok(Awesome::allocate(lua, class)?.build())
    }
}

impl Display for AwesomeState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Awesome: {:p}", self)
    }
}

impl <'lua> ToLua<'lua> for Awesome<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.0.to_lua(lua)
    }
}

impl UserData for AwesomeState {}

pub fn init(lua: &Lua) -> rlua::Result<Class> {
    property_setup(lua, method_setup(lua, Class::builder(lua, Some(Rc::new(Awesome::new)), None, None)?)?)?
        .save_class("awesome")?
        .build()
}

fn method_setup<'lua>(lua: &'lua Lua, builder: ClassBuilder<'lua>) -> rlua::Result<ClassBuilder<'lua>> {
    // TODO Do properly
    use super::dummy;
    builder.method("connect_signal".into(), lua.create_function(dummy))?
           .method("register_xproperty".into(), lua.create_function(register_xproperty))?
           .method("xkb_get_group_names".into(), lua.create_function(xkb_get_group_names))?
           .method("restart".into(), lua.create_function(restart))?
           .method("load_image".into(), lua.create_function(dummy))?
           .method("quit".into(), lua.create_function(dummy))
}

/// Registers a new X property
/// This actually does nothing, since this is Wayland.
fn register_xproperty<'lua>(_: &'lua Lua, _: Value<'lua>) -> rlua::Result<()> {
    warn!("register_xproperty not supported");
    Ok(())
}

/// Get layout short names
fn xkb_get_group_names<'lua>(_: &'lua Lua, _: ()) -> rlua::Result<()> {
    warn!("xkb_get_group_names not supported");
    Ok(())
}

/// Restart Awesome by restarting the Lua thread
fn restart<'lua>(_: &'lua Lua, _: ()) -> rlua::Result<()> {
    use lua::{self, LuaQuery};
    if let Err(err) = lua::send(LuaQuery::Restart) {
        warn!("Could not restart Lua thread {:#?}", err);
    }
    Ok(())
}

fn property_setup<'lua>(lua: &'lua Lua, builder: ClassBuilder<'lua>) -> rlua::Result<ClassBuilder<'lua>> {
    // TODO Do properly
    builder.dummy_property("version".into(), "0".to_lua(lua)?)?
           .dummy_property("themes_path".into(), "/usr/share/awesome/themes".to_lua(lua)?)?
           .dummy_property("conffile".into(), "".to_lua(lua)?)
}

impl_objectable!(Awesome, AwesomeState);
<|endoftext|>


<commit_before>extern crate env_logger;
extern crate pretty;
extern crate difference;
#[macro_use]
extern crate pretty_assertions;

extern crate gluon_parser as parser;
extern crate gluon_base as base;

use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;

use difference::assert_diff;

use parser::format_expr;

mod support;

fn test_format(name: &str) {
    let _ = env_logger::init();

    let name = Path::new(name);
    let mut contents = String::new();
    File::open(Path::new("../").join(name))
        .unwrap()
        .read_to_string(&mut contents)
        .unwrap();

    let out_str = format_expr(&contents).unwrap();

    if contents != out_str {
        let out_path = Path::new(env!("OUT_DIR")).join(name.file_name().unwrap());
        File::create(out_path)
            .unwrap()
            .write_all(out_str.as_bytes())
            .unwrap();
        assert_diff(&contents, &out_str, " ", 0);
    }
}

#[test]
fn map() {
    test_format("std/map.glu");
}

#[test]
fn prelude() {
    test_format("std/prelude.glu");
}

#[test]
fn state() {
    test_format("std/state.glu");
}

#[test]
fn stream() {
    test_format("std/stream.glu");
}

#[test]
fn string() {
    test_format("std/string.glu");
}

#[test]
fn test() {
    test_format("std/test.glu");
}

#[test]
fn types() {
    test_format("std/types.glu");
}

#[test]
fn writer() {
    test_format("std/writer.glu");
}

#[test]
fn repl() {
    test_format("repl/src/repl.glu");
}

#[test]
fn dont_add_newline_for_let_literal() {
    let expr = r#"
let x = 1
x
"#;
    assert_eq!(
        &format_expr(expr).unwrap(),
        r#"
let x = 1
x
"#
    );
}

#[test]
fn dont_lose_information_in_literals() {
    let expr = r#"
3.14 "\t\n\r\""
"#;
    assert_eq!(&format_expr(expr).unwrap(), expr);
}

#[test]
fn preserve_comment_between_let_in() {
    let expr = r#"
// test
let x = 1
// test
type Test = Int
// test
1
// test2
"#;
    assert_eq!(&format_expr(expr).unwrap(), expr);
}

#[test]
fn preserve_whitespace_in_record() {
    let expr = r#"
{
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax = 1,

    bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbby = 2
}
"#;
    assert_eq!(&format_expr(expr).unwrap(), expr);
}

#[test]
fn preserve_block_comments() {
    let expr = r#"
/* test */
let x = { field = f /* test */ 123 /* doc */ }
/* test */
x
"#;
    assert_eq!(&format_expr(expr).unwrap(), expr);
}

#[test]
fn preserve_more_block_comments() {
    let expr = r#"
{ /* abc */ field /* abc */ = /* test */ 123 }
"#;
    assert_eq!(&format_expr(expr).unwrap(), expr);
}

#[test]
fn preserve_shebang_line() {
    let expr = r#"#!/bin/gluon
/* test */
let x = { field = f /* test */ 123 /* doc */ }
/* test */
x
"#;
    assert_eq!(&format_expr(expr).unwrap(), expr);
}

#[test]
fn nested_constructor_pattern() {
    let expr = r#"
match None with
| Some (Some x) -> x
| _ -> 123
"#;
    assert_eq!(&format_expr(expr).unwrap(), expr);
}

#[test]
fn long_pattern_match() {
    let expr = r#"
let {
    CCCCCCCCCCCCCC,
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
    bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
} =
    test
123
"#;
    assert_eq!(&format_expr(expr).unwrap(), expr);
}<commit_msg>Ignore the block comment test that was added<commit_after>extern crate env_logger;
extern crate pretty;
extern crate difference;
#[macro_use]
extern crate pretty_assertions;

extern crate gluon_parser as parser;
extern crate gluon_base as base;

use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;

use difference::assert_diff;

use parser::format_expr;

mod support;

fn test_format(name: &str) {
    let _ = env_logger::init();

    let name = Path::new(name);
    let mut contents = String::new();
    File::open(Path::new("../").join(name))
        .unwrap()
        .read_to_string(&mut contents)
        .unwrap();

    let out_str = format_expr(&contents).unwrap();

    if contents != out_str {
        let out_path = Path::new(env!("OUT_DIR")).join(name.file_name().unwrap());
        File::create(out_path)
            .unwrap()
            .write_all(out_str.as_bytes())
            .unwrap();
        assert_diff(&contents, &out_str, " ", 0);
    }
}

#[test]
fn map() {
    test_format("std/map.glu");
}

#[test]
fn prelude() {
    test_format("std/prelude.glu");
}

#[test]
fn state() {
    test_format("std/state.glu");
}

#[test]
fn stream() {
    test_format("std/stream.glu");
}

#[test]
fn string() {
    test_format("std/string.glu");
}

#[test]
fn test() {
    test_format("std/test.glu");
}

#[test]
fn types() {
    test_format("std/types.glu");
}

#[test]
fn writer() {
    test_format("std/writer.glu");
}

#[test]
fn repl() {
    test_format("repl/src/repl.glu");
}

#[test]
fn dont_add_newline_for_let_literal() {
    let expr = r#"
let x = 1
x
"#;
    assert_eq!(
        &format_expr(expr).unwrap(),
        r#"
let x = 1
x
"#
    );
}

#[test]
fn dont_lose_information_in_literals() {
    let expr = r#"
3.14 "\t\n\r\""
"#;
    assert_eq!(&format_expr(expr).unwrap(), expr);
}

#[test]
fn preserve_comment_between_let_in() {
    let expr = r#"
// test
let x = 1
// test
type Test = Int
// test
1
// test2
"#;
    assert_eq!(&format_expr(expr).unwrap(), expr);
}

#[test]
fn preserve_whitespace_in_record() {
    let expr = r#"
{
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax = 1,

    bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbby = 2
}
"#;
    assert_eq!(&format_expr(expr).unwrap(), expr);
}

#[test]
fn preserve_block_comments() {
    let expr = r#"
/* test */
let x = { field = f /* test */ 123 /* doc */ }
/* test */
x
"#;
    assert_eq!(&format_expr(expr).unwrap(), expr);
}

// TODO
#[ignore]
#[test]
fn preserve_more_block_comments() {
    let expr = r#"
{ /* abc */ field /* abc */ = /* test */ 123 }
"#;
    assert_eq!(&format_expr(expr).unwrap(), expr);
}

#[test]
fn preserve_shebang_line() {
    let expr = r#"#!/bin/gluon
/* test */
let x = { field = f /* test */ 123 /* doc */ }
/* test */
x
"#;
    assert_eq!(&format_expr(expr).unwrap(), expr);
}

#[test]
fn nested_constructor_pattern() {
    let expr = r#"
match None with
| Some (Some x) -> x
| _ -> 123
"#;
    assert_eq!(&format_expr(expr).unwrap(), expr);
}

#[test]
fn long_pattern_match() {
    let expr = r#"
let {
    CCCCCCCCCCCCCC,
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
    bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
} =
    test
123
"#;
    assert_eq!(&format_expr(expr).unwrap(), expr);
}
<|endoftext|>


<commit_before>//
// OpenAOE: An open source reimplementation of Age of Empires (1997)
// Copyright (c) 2016 Kevin Fuller
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// i32he above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// i32HE SOFi32WARE IS PROVIDED "AS IS", WIi32HOUi32 WARRANi32Y OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUi32 NOi32 LIMIi32ED i32O i32HE WARRANi32IES OF MERCHANi32ABILIi32Y,
// FIi32NESS FOR A PARi32ICULAR PURPOSE AND NONINFRINGEMENi32. IN NO EVENi32 SHALL i32HE
// AUi32HORS OR COPYRIGHi32 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR Oi32HER
// LIABILIi32Y, WHEi32HER IN AN ACi32ION OF CONi32RACi32, i32ORi32 OR Oi32HERWISE, ARISING FROM,
// OUi32 OF OR IN CONNECi32ION WIi32H i32HE SOFi32WARE OR i32HE USE OR Oi32HER DEALINGS IN i32HE
// SOFi32WARE.
//

use sdl2;

use std::cmp;
use std::ops;

#[derive(Default, Copy, Clone)]
pub struct Rect {
    pub x: i32,
    pub y: i32,
    pub w: i32,
    pub h: i32,
}

impl Rect {
    pub fn new() -> Rect {
        Default::default()
    }

    pub fn of(x: i32, y: i32, w: i32, h: i32) -> Rect {
        Rect { x: x, y: y, w: w, h: h, }
    }

    pub fn translate(&mut self, x: i32, y: i32) {
        self.x += x;
        self.y += y;
    }

    pub fn extend(&mut self, other: &Rect) {
        self.x = cmp::min(self.x, other.x);
        self.y = cmp::min(self.y, other.y);
        self.w = cmp::max(self.x + self.w, other.x + other.w) - self.x;
        self.h = cmp::max(self.y + self.h, other.y + other.h) - self.y;
    }
}

impl Into<sdl2::rect::Rect> for Rect {
    fn into(self) -> sdl2::rect::Rect {
        sdl2::rect::Rect::new(self.x, self.y, self.w as u32, self.h as u32)
    }
}
<commit_msg>Fix copyright header and remove unused import in Rect<commit_after>//
// OpenAOE: An open source reimplementation of Age of Empires (1997)
// Copyright (c) 2016 Kevin Fuller
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//

use sdl2;

use std::cmp;

#[derive(Default, Copy, Clone)]
pub struct Rect {
    pub x: i32,
    pub y: i32,
    pub w: i32,
    pub h: i32,
}

impl Rect {
    pub fn new() -> Rect {
        Default::default()
    }

    pub fn of(x: i32, y: i32, w: i32, h: i32) -> Rect {
        Rect { x: x, y: y, w: w, h: h, }
    }

    pub fn translate(&mut self, x: i32, y: i32) {
        self.x += x;
        self.y += y;
    }

    pub fn extend(&mut self, other: &Rect) {
        self.x = cmp::min(self.x, other.x);
        self.y = cmp::min(self.y, other.y);
        self.w = cmp::max(self.x + self.w, other.x + other.w) - self.x;
        self.h = cmp::max(self.y + self.h, other.y + other.h) - self.y;
    }
}

impl Into<sdl2::rect::Rect> for Rect {
    fn into(self) -> sdl2::rect::Rect {
        sdl2::rect::Rect::new(self.x, self.y, self.w as u32, self.h as u32)
    }
}
<|endoftext|>


<commit_before>use geom::size::Size2D;

pub trait ShareContext {
    // Creates a new context for GL object sharing.
    static fn new(size: Size2D<int>) -> Self;

    // Flushes the context.
    fn flush(&self);

    // Returns the platform-specific ID that can be passed to other processes to access the shared
    // resources.
    fn id() -> int;
}

<commit_msg>Update tests for language changes<commit_after>use geom::size::Size2D;

pub trait ShareContext {
    // Creates a new context for GL object sharing.
    static fn new(size: Size2D<int>) -> Self;

    // Flushes the context.
    fn flush(&self);

    // Returns the platform-specific ID that can be passed to other processes to access the shared
    // resources.
    fn id() -> int;
}

#[test]
fn smoke() {}

<|endoftext|>


<commit_before>use std::io::{Result, Write};

use analysis;
use analysis::namespaces::MAIN;
use env::Env;
use file_saver::save_to_file;
use library::*;
use library::Type as LType;
use writer::primitives;

use stripper_interface as stripper;
use stripper_interface::Type as SType;
use stripper_interface::TypeStruct;
use stripper_interface::{FILE, /*FILE_COMMENT, */MOD_COMMENT};

mod format;

trait FunctionTraitType {
    fn functions(&self) -> &Vec<Function>;
    fn name(&self) -> &str;
}

macro_rules! impl_function_trait_type {
    ($ty:ident) => {
        impl FunctionTraitType for $ty {
            fn functions(&self) -> &Vec<Function> {
                &self.functions
            }

            fn name(&self) -> &str {
                &self.name
            }
        }
    }
}

impl_function_trait_type!(Class);
impl_function_trait_type!(Interface);

trait ToStripperType {
    fn convert(&self) -> stripper::TypeStruct;
    fn doc(&self) -> Option<String>;
    fn doc_deprecated(&self) -> Option<String>;
}

macro_rules! impl_to_stripper_type {
    ($ty:ident, $enum_var:ident) => {
        impl ToStripperType for $ty {
            fn convert(&self) -> TypeStruct {
                TypeStruct::new(SType::$enum_var, &self.name)
            }

            fn doc(&self) -> Option<String> {
                self.doc.clone()
            }

            fn doc_deprecated(&self) -> Option<String> {
                self.doc_deprecated.clone()
            }
        }
    }
}

impl_to_stripper_type!(Alias, Struct);
impl_to_stripper_type!(Constant, Const);
impl_to_stripper_type!(Member, Variant);
impl_to_stripper_type!(Enumeration, Enum);
impl_to_stripper_type!(Bitfield, Type);
impl_to_stripper_type!(Record, Type);
impl_to_stripper_type!(Field, Variant);
impl_to_stripper_type!(Union, Struct);
impl_to_stripper_type!(Function, Fn);
impl_to_stripper_type!(Interface, Trait);
impl_to_stripper_type!(Class, Struct);
impl_to_stripper_type!(Namespace, Mod);
impl_to_stripper_type!(Parameter, Variant);

pub fn generate(env: &Env) {
    let path =  env.config.target_path.join("doc.cmts");
    println!("Generating documentation {:?}", path);
    save_to_file(&path, env.config.make_backup,
        |w| generate_doc(w, &env));
}

fn generate_doc(mut w: &mut Write, env: &Env) -> Result<()> {
    try!(writeln!(w, "{}*", FILE));

    let namespace = env.library.namespace(MAIN);
    for obj in env.config.objects.values() {
        if !obj.status.need_generate() {
            continue;
        }

        let info = analysis::object::class(env, obj)
            .or_else(|| analysis::object::interface(env, obj));
        if let Some(info) = info {
            try!(create_object_doc(w, env, &info));
        }
    }

    for ty in namespace.types.iter().filter_map(|t| t.as_ref()) {
        try!(handle_type(&mut w, &ty, &env));
    }
    Ok(())
}

fn handle_type(w: &mut Write, ty: &LType, env: &Env) -> Result<()> {
    match *ty {
        LType::Alias(ref a) => create_sub_doc(w, a, env),
        LType::Enumeration(ref e) => create_enum_doc(w, &e, env),
        LType::Function(ref f) => create_fn_doc(w, &f, None, env),
        _ => Ok(()),
    }
}

fn create_class_doc<T: FunctionTraitType + ToStripperType>(w: &mut Write, class: &T,
                                                           parent: Option<Box<TypeStruct>>,
                                                           has_trait: bool,
                                                           env: &Env)
                                                          -> Result<()> {
    let tabs : String = primitives::tabs(compute_indent(&parent));
    let ty = TypeStruct { parent: parent, ..class.convert() };
    let ty_ext = TypeStruct {
        ty: SType::Trait,
        name: format!("{}Ext", ty.name),
        ..ty.clone()
    };

    //try!(writeln!(w, "{}src/auto/{}.rs", FILE, module_name(&class.name())));
    if class.doc().is_some() || class.doc_deprecated().is_some() {
        try!(writeln!(w, "{}{}", MOD_COMMENT, ty));
    }

    if let Some(ref class_doc) = class.doc() {
        try!(write_lines(w, &class_doc, &tabs, env));
    }
    if let Some(ref class_doc) = class.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &class_doc, &tabs, env));
    }

    let ty = TypeStruct { ty: SType::Impl, ..ty };

    for function in class.functions().iter() {
        let ty = if has_trait && function.parameters.iter().any(|p| p.instance_parameter) {
            ty_ext.clone()
        }
        else {
            ty.clone()
        };
        try!(create_fn_doc(w, &function, Some(Box::new(ty)), env));
    }
    Ok(())
}

fn create_enum_doc(w: &mut Write, enum_: &Enumeration, env: &Env) -> Result<()> {
    let ty = enum_.convert();
    let tabs = "";

    if enum_.doc().is_some() || enum_.doc_deprecated().is_some() {
        try!(writeln!(w, "{}{}", MOD_COMMENT, ty));
    }

    if let Some(ref enum_doc) = enum_.doc() {
        try!(write_lines(w, &enum_doc, &tabs, env));
    }
    if let Some(ref enum_doc) = enum_.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &enum_doc, &tabs, env));
    }

    let tabs = "\t";
    for member_doc in enum_.members.iter() {
        let mut sub_ty : TypeStruct = member_doc.convert();

        if member_doc.doc().is_some() || member_doc.doc_deprecated().is_some() {
            sub_ty.parent = Some(Box::new(ty.clone()));
            try!(writeln!(w, "{}{}", MOD_COMMENT, sub_ty));
        }
        if let Some(ref m_doc) = member_doc.doc() {
            try!(write_lines(w, &m_doc, &tabs, env));
        }
        if let Some(ref m_doc) = member_doc.doc_deprecated() {
            try!(write_header(w, &tabs, "Deprecated"));
            try!(write_lines(w, &m_doc, &tabs, env));
        }
    }
    Ok(())
}

fn create_fn_doc(w: &mut Write, fn_: &Function, parent: Option<Box<TypeStruct>>,
                 env: &Env) -> Result<()> {
    let tabs : String = primitives::tabs(compute_indent(&parent) + 1);

    if fn_.doc().is_none() && fn_.doc_deprecated().is_none() && fn_.ret.doc().is_none() {
        if fn_.parameters.iter().all(|x| {
            x.doc().is_none()
        }) {
            return Ok(());
        }
    }
    let mut sub_ty = fn_.convert();
    sub_ty.parent = parent;
    try!(writeln!(w, "{}{}", MOD_COMMENT, sub_ty));

    if let Some(ref docs) = fn_.doc() {
        try!(write_lines(w, &docs, &tabs, env));
    };
    if let Some(ref docs) = fn_.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &docs, &tabs, env));
    };

    if fn_.parameters.iter().any(|x| {
           x.instance_parameter == false && !x.name.is_empty() && x.doc().is_some()
       }) {
        try!(write_header(w, &tabs, "Parameters"));
    }
    for parameter in fn_.parameters.iter() {
        if parameter.instance_parameter || parameter.name.is_empty() {
            continue
        }
        if let Some(ref parameter_doc) = parameter.doc() {
            try!(writeln!(w, "{}/// ## {}:", tabs, parameter.name));
            try!(write_lines(w, &parameter_doc, &tabs, env));
        }
    }

    if let Some(ref doc) = fn_.ret.doc() {
        try!(write_header(w, &tabs, "Returns"));
        try!(write_lines(w, &doc, &tabs, env));
    }
    Ok(())
}

fn write_lines(w: &mut Write, lines: &str, tabs: &str,
               env: &Env) -> Result<()> {
    for line in format::reformat_doc(&lines, env).split("\n") {
        try!(writeln!(w, "{}/// {}", tabs, line));
    }
    Ok(())
}

fn create_sub_doc<T: ToStripperType>(w: &mut Write, ty: &T, env: &Env) -> Result<()> {
    let tabs = "";
    let sub_ty = ty.convert();

    if ty.doc().is_some() || ty.doc_deprecated().is_some() {
        try!(writeln!(w, "{}{}", MOD_COMMENT, sub_ty));
    }

    if let Some(doc) = ty.doc() {
        try!(write_lines(w, &doc, &tabs, env));
    }
    if let Some(doc) = ty.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &doc, &tabs, env));
    }
    Ok(())
}

fn compute_indent(e: &Option<Box<TypeStruct>>) -> usize {
    match *e {
        Some(ref e) if e.parent.is_some() => compute_indent(&e.parent) + 1,
        _ => 0,
    }
}

fn write_header(w: &mut Write, tabs: &str, header: &str) -> Result<()> {
    writeln!(w, "{0}///\n{0}/// # {1}\n{0}///", tabs, header)
}
<commit_msg>Document traits<commit_after>use std::io::{Result, Write};

use analysis;
use analysis::namespaces::MAIN;
use env::Env;
use file_saver::save_to_file;
use library::*;
use library::Type as LType;
use writer::primitives;

use stripper_interface as stripper;
use stripper_interface::Type as SType;
use stripper_interface::TypeStruct;
use stripper_interface::{FILE, /*FILE_COMMENT, */MOD_COMMENT};

mod format;

trait FunctionTraitType {
    fn functions(&self) -> &Vec<Function>;
    fn name(&self) -> &str;
}

macro_rules! impl_function_trait_type {
    ($ty:ident) => {
        impl FunctionTraitType for $ty {
            fn functions(&self) -> &Vec<Function> {
                &self.functions
            }

            fn name(&self) -> &str {
                &self.name
            }
        }
    }
}

impl_function_trait_type!(Class);
impl_function_trait_type!(Interface);

trait ToStripperType {
    fn convert(&self) -> stripper::TypeStruct;
    fn doc(&self) -> Option<String>;
    fn doc_deprecated(&self) -> Option<String>;
}

macro_rules! impl_to_stripper_type {
    ($ty:ident, $enum_var:ident) => {
        impl ToStripperType for $ty {
            fn convert(&self) -> TypeStruct {
                TypeStruct::new(SType::$enum_var, &self.name)
            }

            fn doc(&self) -> Option<String> {
                self.doc.clone()
            }

            fn doc_deprecated(&self) -> Option<String> {
                self.doc_deprecated.clone()
            }
        }
    }
}

impl_to_stripper_type!(Alias, Struct);
impl_to_stripper_type!(Constant, Const);
impl_to_stripper_type!(Member, Variant);
impl_to_stripper_type!(Enumeration, Enum);
impl_to_stripper_type!(Bitfield, Type);
impl_to_stripper_type!(Record, Type);
impl_to_stripper_type!(Field, Variant);
impl_to_stripper_type!(Union, Struct);
impl_to_stripper_type!(Function, Fn);
impl_to_stripper_type!(Interface, Trait);
impl_to_stripper_type!(Class, Struct);
impl_to_stripper_type!(Namespace, Mod);
impl_to_stripper_type!(Parameter, Variant);

pub fn generate(env: &Env) {
    let path =  env.config.target_path.join("doc.cmts");
    println!("Generating documentation {:?}", path);
    save_to_file(&path, env.config.make_backup,
        |w| generate_doc(w, &env));
}

fn generate_doc(mut w: &mut Write, env: &Env) -> Result<()> {
    try!(writeln!(w, "{}*", FILE));

    let namespace = env.library.namespace(MAIN);
    for obj in env.config.objects.values() {
        if !obj.status.need_generate() {
            continue;
        }

        let info = analysis::object::class(env, obj)
            .or_else(|| analysis::object::interface(env, obj));
        if let Some(info) = info {
            try!(create_object_doc(w, env, &info));
        }
    }

    for ty in namespace.types.iter().filter_map(|t| t.as_ref()) {
        try!(handle_type(&mut w, &ty, &env));
    }
    Ok(())
}

fn handle_type(w: &mut Write, ty: &LType, env: &Env) -> Result<()> {
    match *ty {
        LType::Alias(ref a) => create_sub_doc(w, a, env),
        LType::Enumeration(ref e) => create_enum_doc(w, &e, env),
        LType::Function(ref f) => create_fn_doc(w, &f, None, env),
        _ => Ok(()),
    }
}

fn create_object_doc(w: &mut Write, env: &Env, info: &analysis::object::Info) -> Result<()> {
    let tabs = "";
    let ty = TypeStruct::new(SType::Struct, &info.name);
    let ty_ext = TypeStruct::new(SType::Trait, &format!("{}Ext", info.name));
    let has_trait = info.has_children;
    let class: &ToStripperType;
    let functions: &[Function];
    let implements: Vec<TypeId>;

    match *env.library.type_(info.type_id) {
        Type::Class(ref cl) => {
            class = cl;
            functions = &cl.functions;
            implements = cl.parents.iter()
                .chain(cl.implements.iter())
                .map(|&tid| tid)
                .collect();
        }
        Type::Interface(ref iface) => {
            class = iface;
            functions = &iface.functions;
            implements = iface.prereq_parents.clone();
        }
        _ => unreachable!(),
    }

    try!(writeln!(w, "{}{}", MOD_COMMENT, ty));

    if let Some(ref class_doc) = class.doc() {
        try!(write_lines(w, &class_doc, &tabs, env));
    }

    try!(write_header(w, &tabs, "Implements"));
    let implements = implements.iter()
        .filter(|&tid| !env.type_status(&tid.full_name(&env.library)).ignored())
        .map(|&tid| format!("`{}Ext`", env.library.type_(tid).get_name()))
        .collect::<Vec<_>>();
    try!(write_verbatim(w, &implements.join(", "), &tabs));

    if let Some(ref class_doc) = class.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &class_doc, &tabs, env));
    }

    if has_trait {
        try!(writeln!(w, "{}{}", MOD_COMMENT, ty_ext));
        try!(write_verbatim(w, &format!("Trait containing all `{}` methods.", ty.name), &tabs));
    }

    let ty = TypeStruct { ty: SType::Impl, ..ty };

    for function in functions {
        let ty = if has_trait && function.parameters.iter().any(|p| p.instance_parameter) {
            ty_ext.clone()
        }
        else {
            ty.clone()
        };
        try!(create_fn_doc(w, &function, Some(Box::new(ty)), env));
    }
    Ok(())
}

fn create_enum_doc(w: &mut Write, enum_: &Enumeration, env: &Env) -> Result<()> {
    let ty = enum_.convert();
    let tabs = "";

    if enum_.doc().is_some() || enum_.doc_deprecated().is_some() {
        try!(writeln!(w, "{}{}", MOD_COMMENT, ty));
    }

    if let Some(ref enum_doc) = enum_.doc() {
        try!(write_lines(w, &enum_doc, &tabs, env));
    }
    if let Some(ref enum_doc) = enum_.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &enum_doc, &tabs, env));
    }

    let tabs = "\t";
    for member_doc in enum_.members.iter() {
        let mut sub_ty : TypeStruct = member_doc.convert();

        if member_doc.doc().is_some() || member_doc.doc_deprecated().is_some() {
            sub_ty.parent = Some(Box::new(ty.clone()));
            try!(writeln!(w, "{}{}", MOD_COMMENT, sub_ty));
        }
        if let Some(ref m_doc) = member_doc.doc() {
            try!(write_lines(w, &m_doc, &tabs, env));
        }
        if let Some(ref m_doc) = member_doc.doc_deprecated() {
            try!(write_header(w, &tabs, "Deprecated"));
            try!(write_lines(w, &m_doc, &tabs, env));
        }
    }
    Ok(())
}

fn create_fn_doc(w: &mut Write, fn_: &Function, parent: Option<Box<TypeStruct>>,
                 env: &Env) -> Result<()> {
    let tabs : String = primitives::tabs(compute_indent(&parent) + 1);

    if fn_.doc().is_none() && fn_.doc_deprecated().is_none() && fn_.ret.doc().is_none() {
        if fn_.parameters.iter().all(|x| {
            x.doc().is_none()
        }) {
            return Ok(());
        }
    }
    let mut sub_ty = fn_.convert();
    sub_ty.parent = parent;
    try!(writeln!(w, "{}{}", MOD_COMMENT, sub_ty));

    if let Some(ref docs) = fn_.doc() {
        try!(write_lines(w, &docs, &tabs, env));
    };
    if let Some(ref docs) = fn_.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &docs, &tabs, env));
    };

    if fn_.parameters.iter().any(|x| {
           x.instance_parameter == false && !x.name.is_empty() && x.doc().is_some()
       }) {
        try!(write_header(w, &tabs, "Parameters"));
    }
    for parameter in fn_.parameters.iter() {
        if parameter.instance_parameter || parameter.name.is_empty() {
            continue
        }
        if let Some(ref parameter_doc) = parameter.doc() {
            try!(writeln!(w, "{}/// ## {}:", tabs, parameter.name));
            try!(write_lines(w, &parameter_doc, &tabs, env));
        }
    }

    if let Some(ref doc) = fn_.ret.doc() {
        try!(write_header(w, &tabs, "Returns"));
        try!(write_lines(w, &doc, &tabs, env));
    }
    Ok(())
}

fn write_lines(w: &mut Write, lines: &str, tabs: &str, env: &Env) -> Result<()> {
    write_verbatim(w, &format::reformat_doc(&lines, env), tabs)
}

fn write_verbatim(w: &mut Write, lines: &str, tabs: &str) -> Result<()> {
    for line in lines.split("\n") {
        try!(writeln!(w, "{}/// {}", tabs, line));
    }
    Ok(())
}

fn create_sub_doc<T: ToStripperType>(w: &mut Write, ty: &T, env: &Env) -> Result<()> {
    let tabs = "";
    let sub_ty = ty.convert();

    if ty.doc().is_some() || ty.doc_deprecated().is_some() {
        try!(writeln!(w, "{}{}", MOD_COMMENT, sub_ty));
    }

    if let Some(doc) = ty.doc() {
        try!(write_lines(w, &doc, &tabs, env));
    }
    if let Some(doc) = ty.doc_deprecated() {
        try!(write_header(w, &tabs, "Deprecated"));
        try!(write_lines(w, &doc, &tabs, env));
    }
    Ok(())
}

fn compute_indent(e: &Option<Box<TypeStruct>>) -> usize {
    match *e {
        Some(ref e) if e.parent.is_some() => compute_indent(&e.parent) + 1,
        _ => 0,
    }
}

fn write_header(w: &mut Write, tabs: &str, header: &str) -> Result<()> {
    writeln!(w, "{0}///\n{0}/// # {1}\n{0}///", tabs, header)
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{
    common::{
        check_network, get_block_index_from_request, get_timestamp, handle_request, with_context,
    },
    error::{ApiError, ApiResult},
    types::{Block, BlockIdentifier, BlockRequest, BlockResponse, Transaction},
    RosettaContext,
};
use aptos_logger::{debug, trace};
use aptos_rest_client::aptos_api_types::{BlockInfo, HashValue};
use std::{
    cmp::Ordering,
    collections::BTreeMap,
    sync::{Arc, RwLock},
};
use warp::Filter;

pub fn block_route(
    server_context: RosettaContext,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    warp::path!("block")
        .and(warp::post())
        .and(warp::body::json())
        .and(with_context(server_context))
        .and_then(handle_request(block))
}

/// Retrieves a block (in this case a single transaction) given it's identifier.
///
/// Our implementation allows for by `index`, which is the ledger `version` or by
/// transaction `hash`.
///
/// [API Spec](https://www.rosetta-api.org/docs/BlockApi.html#block)
async fn block(request: BlockRequest, server_context: RosettaContext) -> ApiResult<BlockResponse> {
    debug!("/block");
    trace!(
        request = ?request,
        server_context = ?server_context,
        "/block",
    );

    check_network(request.network_identifier, &server_context)?;

    let rest_client = server_context.rest_client()?;

    // Retrieve by block or by hash, both or neither is not allowed
    let block_index =
        get_block_index_from_request(&server_context, request.block_identifier).await?;

    let (parent_transaction, block_info, transactions) = get_block_by_index(
        server_context.block_cache()?.as_ref(),
        &rest_client,
        block_index,
    )
    .await?;

    let block = build_block(server_context, parent_transaction, block_info, transactions).await?;

    Ok(BlockResponse {
        block: Some(block),
        other_transactions: None,
    })
}

/// Build up the transaction, which should contain the `operations` as the change set
async fn build_block(
    server_context: RosettaContext,
    parent_block_identifier: BlockIdentifier,
    block_info: BlockInfo,
    transactions: Vec<aptos_rest_client::Transaction>,
) -> ApiResult<Block> {
    // note: timestamps are in microseconds, so we convert to milliseconds
    let timestamp = get_timestamp(block_info);
    let block_identifier = BlockIdentifier::from_block_info(block_info);

    // Convert the transactions and build the block
    let mut txns: Vec<Transaction> = Vec::new();
    for txn in transactions {
        txns.push(
            Transaction::from_transaction(
                server_context.coin_cache.clone(),
                server_context.rest_client()?.as_ref(),
                txn,
            )
            .await?,
        )
    }

    Ok(Block {
        block_identifier,
        parent_block_identifier,
        timestamp,
        transactions: txns,
    })
}

/// Retrieves a block by its index
async fn get_block_by_index(
    block_cache: &BlockCache,
    rest_client: &aptos_rest_client::Client,
    block_index: u64,
) -> ApiResult<(
    BlockIdentifier,
    BlockInfo,
    Vec<aptos_rest_client::Transaction>,
)> {
    // For the genesis block, we populate parent_block_identifier with the
    // same genesis block. Refer to
    // https://www.rosetta-api.org/docs/common_mistakes.html#malformed-genesis-block
    if block_index == 0 {
        let block_info = block_cache.get_block_info(block_index).await?;
        let response = rest_client.get_transaction_by_version(0).await?;
        let txn = response.into_inner();
        Ok((
            BlockIdentifier::from_block_info(block_info),
            block_info,
            vec![txn],
        ))
    } else {
        // Retrieve the previous block's identifier
        let prev_block_info = block_cache.get_block_info(block_index - 1).await?;
        let prev_block = BlockIdentifier::from_block_info(prev_block_info);

        // Retrieve the current block
        let block_info = block_cache.get_block_info(block_index).await?;
        let txns = rest_client
            .get_transactions(
                Some(block_info.start_version),
                Some(block_info.num_transactions),
            )
            .await?
            .into_inner();
        Ok((prev_block, block_info, txns))
    }
}

/// A cache of [`BlockInfo`] to allow us to keep track of the block boundaries
#[derive(Debug)]
pub struct BlockCache {
    blocks: RwLock<BTreeMap<u64, BlockInfo>>,
    hashes: RwLock<BTreeMap<HashValue, u64>>,
    versions: RwLock<BTreeMap<u64, u64>>,
    rest_client: Arc<aptos_rest_client::Client>,
}

impl BlockCache {
    pub async fn new(rest_client: Arc<aptos_rest_client::Client>) -> ApiResult<Self> {
        let mut blocks = BTreeMap::new();
        let mut hashes = BTreeMap::new();
        let mut versions = BTreeMap::new();
        // Genesis is always index 0
        let genesis_block_info = BlockInfo {
            block_height: 0,
            block_hash: aptos_crypto::HashValue::zero().into(),
            block_timestamp: 0,
            start_version: 0,
            end_version: 0,
            num_transactions: 1,
        };
        let hash = genesis_block_info.block_hash;
        blocks.insert(0, genesis_block_info);
        hashes.insert(hash, 0);
        versions.insert(0, 0);

        // Now insert the first and last blocks
        let state = rest_client.get_ledger_information().await?.into_inner();
        let block_cache = BlockCache {
            blocks: RwLock::new(blocks),
            hashes: RwLock::new(hashes),
            versions: RwLock::new(versions),
            rest_client,
        };
        if let Some(oldest_ledger_version) = state.oldest_ledger_version {
            block_cache.add_block(oldest_ledger_version).await?;
        }
        block_cache.add_block(state.version).await?;

        Ok(block_cache)
    }

    /// Retrieve the block info for the index
    pub async fn get_block_info(&self, block_index: u64) -> ApiResult<BlockInfo> {
        // If we already have the block info, let's roll with it
        let (start_version, range, traverse_right) = {
            let map = self.blocks.read().unwrap();
            if let Some(block_info) = map.get(&block_index) {
                return Ok(*block_info);
            }

            // Find the closest, left or right, and build up blocks from there
            // Search from right to left because we're less likely to search far into the past
            let mut search_left = None;
            let mut search_right = None;
            for (i, info) in map.iter().rev() {
                let i = *i;
                match i.cmp(&block_index) {
                    Ordering::Less => {
                        let start_version = info.end_version.saturating_add(1);
                        let block_range = i.saturating_add(1)..=block_index;
                        let distance = block_index.saturating_sub(i);
                        search_left = Some((start_version, block_range, distance));
                        // We've passed our block_index so we can stop now
                        break;
                    }
                    Ordering::Greater => {
                        let start_version = info.start_version.saturating_sub(1);
                        let block_range = block_index..=(i.saturating_sub(1));
                        let distance = i.saturating_sub(block_index);
                        search_right = Some((start_version, block_range, distance));
                    }
                    Ordering::Equal => {}
                }
            }

            // Choose the shortest distance and search in that direction
            match (search_left, search_right) {
                (Some((left_version, left_range, _)), None) => (left_version, left_range, true),
                (None, Some((right_version, right_range, _))) => {
                    (right_version, right_range, false)
                }
                (
                    Some((left_version, left_range, left_distance)),
                    Some((right_version, right_range, right_distance)),
                ) => {
                    if left_distance < right_distance {
                        (left_version, left_range, true)
                    } else {
                        (right_version, right_range, false)
                    }
                }
                // This shouldn't happen as the first thing we do when we initialize the cache
                // is put the first and last blocks into it
                _ => unreachable!("There should always be a block in the cache!"),
            }
        };

        // Go through the blocks, and add them into the cache
        let mut running_version = start_version;
        for _ in range {
            let info = self.add_block(running_version).await?;

            // Increment to the next block
            if traverse_right {
                running_version = info.end_version.saturating_add(1);
            } else {
                running_version = info.start_version.saturating_sub(1);
            }
        }

        if let Some(info) = self.blocks.read().unwrap().get(&block_index) {
            Ok(*info)
        } else {
            // If for some reason the block doesn't get found, retry with block incomplete
            Err(ApiError::BlockIncomplete)
        }
    }

    /// Retrieve block info, and add it to the index
    async fn add_block(&self, block_version: u64) -> ApiResult<BlockInfo> {
        let info_response = self.rest_client.get_block_info(block_version).await?;
        let info = info_response.into_inner();

        // Add into the cache (keeping the write lock short)
        let info = {
            let mut map = self.blocks.write().unwrap();
            map.insert(info.block_height, info);
            info
        };

        // Write hash to index mapping
        {
            let mut map = self.hashes.write().unwrap();
            map.insert(info.block_hash, info.block_height);
        }

        // Write version to index mapping
        {
            let mut map = self.versions.write().unwrap();
            map.insert(block_version, info.block_height);
        }
        Ok(info)
    }

    /// Retrieve the block index for the version
    pub async fn get_block_info_by_version(&self, version: u64) -> ApiResult<BlockInfo> {
        // If we already have the version, let's roll with it
        let maybe_index = { self.versions.read().unwrap().get(&version).copied() };

        if let Some(index) = maybe_index {
            if let Some(info) = self.blocks.read().unwrap().get(&index) {
                return Ok(*info);
            }
        }

        // Lookup block info by version
        self.add_block(version).await
    }

    /// Retrieve the block info for the hash
    ///
    /// This is particularly bad, since there's no index on this value.  It can only be derived
    /// from the cache, otherwise it needs to fail immediately.  This cache will need to be saved
    /// somewhere for these purposes.
    ///
    /// We could use the BlockMetadata transaction's hash rather than the block hash as a hack,
    /// and that is always indexed
    ///
    /// TODO: Improve reliability
    pub async fn get_block_index_by_hash(&self, hash: &HashValue) -> ApiResult<u64> {
        if let Some(version) = self.hashes.read().unwrap().get(hash) {
            Ok(*version)
        } else {
            // If for some reason the block doesn't get found, retry with block incomplete
            Err(ApiError::BlockIncomplete)
        }
    }
}
<commit_msg>[aptos-rosetta] Ensure blocks are able to be looked up by beginning and end version<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{
    common::{
        check_network, get_block_index_from_request, get_timestamp, handle_request, with_context,
    },
    error::{ApiError, ApiResult},
    types::{Block, BlockIdentifier, BlockRequest, BlockResponse, Transaction},
    RosettaContext,
};
use aptos_logger::{debug, trace};
use aptos_rest_client::aptos_api_types::{BlockInfo, HashValue};
use std::{
    cmp::Ordering,
    collections::BTreeMap,
    sync::{Arc, RwLock},
};
use warp::Filter;

pub fn block_route(
    server_context: RosettaContext,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    warp::path!("block")
        .and(warp::post())
        .and(warp::body::json())
        .and(with_context(server_context))
        .and_then(handle_request(block))
}

/// Retrieves a block (in this case a single transaction) given it's identifier.
///
/// Our implementation allows for by `index`, which is the ledger `version` or by
/// transaction `hash`.
///
/// [API Spec](https://www.rosetta-api.org/docs/BlockApi.html#block)
async fn block(request: BlockRequest, server_context: RosettaContext) -> ApiResult<BlockResponse> {
    debug!("/block");
    trace!(
        request = ?request,
        server_context = ?server_context,
        "/block",
    );

    check_network(request.network_identifier, &server_context)?;

    let rest_client = server_context.rest_client()?;

    // Retrieve by block or by hash, both or neither is not allowed
    let block_index =
        get_block_index_from_request(&server_context, request.block_identifier).await?;

    let (parent_transaction, block_info, transactions) = get_block_by_index(
        server_context.block_cache()?.as_ref(),
        &rest_client,
        block_index,
    )
    .await?;

    let block = build_block(server_context, parent_transaction, block_info, transactions).await?;

    Ok(BlockResponse {
        block: Some(block),
        other_transactions: None,
    })
}

/// Build up the transaction, which should contain the `operations` as the change set
async fn build_block(
    server_context: RosettaContext,
    parent_block_identifier: BlockIdentifier,
    block_info: BlockInfo,
    transactions: Vec<aptos_rest_client::Transaction>,
) -> ApiResult<Block> {
    // note: timestamps are in microseconds, so we convert to milliseconds
    let timestamp = get_timestamp(block_info);
    let block_identifier = BlockIdentifier::from_block_info(block_info);

    // Convert the transactions and build the block
    let mut txns: Vec<Transaction> = Vec::new();
    for txn in transactions {
        txns.push(
            Transaction::from_transaction(
                server_context.coin_cache.clone(),
                server_context.rest_client()?.as_ref(),
                txn,
            )
            .await?,
        )
    }

    Ok(Block {
        block_identifier,
        parent_block_identifier,
        timestamp,
        transactions: txns,
    })
}

/// Retrieves a block by its index
async fn get_block_by_index(
    block_cache: &BlockCache,
    rest_client: &aptos_rest_client::Client,
    block_index: u64,
) -> ApiResult<(
    BlockIdentifier,
    BlockInfo,
    Vec<aptos_rest_client::Transaction>,
)> {
    // For the genesis block, we populate parent_block_identifier with the
    // same genesis block. Refer to
    // https://www.rosetta-api.org/docs/common_mistakes.html#malformed-genesis-block
    if block_index == 0 {
        let block_info = block_cache.get_block_info(block_index).await?;
        let response = rest_client.get_transaction_by_version(0).await?;
        let txn = response.into_inner();
        Ok((
            BlockIdentifier::from_block_info(block_info),
            block_info,
            vec![txn],
        ))
    } else {
        // Retrieve the previous block's identifier
        let prev_block_info = block_cache.get_block_info(block_index - 1).await?;
        let prev_block = BlockIdentifier::from_block_info(prev_block_info);

        // Retrieve the current block
        let block_info = block_cache.get_block_info(block_index).await?;
        let txns = rest_client
            .get_transactions(
                Some(block_info.start_version),
                Some(block_info.num_transactions),
            )
            .await?
            .into_inner();
        Ok((prev_block, block_info, txns))
    }
}

/// A cache of [`BlockInfo`] to allow us to keep track of the block boundaries
#[derive(Debug)]
pub struct BlockCache {
    blocks: RwLock<BTreeMap<u64, BlockInfo>>,
    hashes: RwLock<BTreeMap<HashValue, u64>>,
    versions: RwLock<BTreeMap<u64, u64>>,
    rest_client: Arc<aptos_rest_client::Client>,
}

impl BlockCache {
    pub async fn new(rest_client: Arc<aptos_rest_client::Client>) -> ApiResult<Self> {
        let mut blocks = BTreeMap::new();
        let mut hashes = BTreeMap::new();
        let mut versions = BTreeMap::new();
        // Genesis is always index 0
        let genesis_block_info = BlockInfo {
            block_height: 0,
            block_hash: aptos_crypto::HashValue::zero().into(),
            block_timestamp: 0,
            start_version: 0,
            end_version: 0,
            num_transactions: 1,
        };
        blocks.insert(0, genesis_block_info);
        hashes.insert(genesis_block_info.block_hash, 0);
        versions.insert(0, 0);

        // Now insert the first and last blocks
        let state = rest_client.get_ledger_information().await?.into_inner();
        let block_cache = BlockCache {
            blocks: RwLock::new(blocks),
            hashes: RwLock::new(hashes),
            versions: RwLock::new(versions),
            rest_client,
        };
        if let Some(oldest_ledger_version) = state.oldest_ledger_version {
            block_cache.add_block(oldest_ledger_version).await?;
        }
        block_cache.add_block(state.version).await?;

        Ok(block_cache)
    }

    /// Retrieve the block info for the index
    pub async fn get_block_info(&self, block_index: u64) -> ApiResult<BlockInfo> {
        // If we already have the block info, let's roll with it
        let (start_version, range, traverse_right) = {
            let map = self.blocks.read().unwrap();
            if let Some(block_info) = map.get(&block_index) {
                return Ok(*block_info);
            }

            // Find the closest, left or right, and build up blocks from there
            // Search from right to left because we're less likely to search far into the past
            let mut search_left = None;
            let mut search_right = None;
            for (i, info) in map.iter().rev() {
                let i = *i;
                match i.cmp(&block_index) {
                    Ordering::Less => {
                        let start_version = info.end_version.saturating_add(1);
                        let block_range = i.saturating_add(1)..=block_index;
                        let distance = block_index.saturating_sub(i);
                        search_left = Some((start_version, block_range, distance));
                        // We've passed our block_index so we can stop now
                        break;
                    }
                    Ordering::Greater => {
                        let start_version = info.start_version.saturating_sub(1);
                        let block_range = block_index..=(i.saturating_sub(1));
                        let distance = i.saturating_sub(block_index);
                        search_right = Some((start_version, block_range, distance));
                    }
                    Ordering::Equal => {}
                }
            }

            // Choose the shortest distance and search in that direction
            match (search_left, search_right) {
                (Some((left_version, left_range, _)), None) => (left_version, left_range, true),
                (None, Some((right_version, right_range, _))) => {
                    (right_version, right_range, false)
                }
                (
                    Some((left_version, left_range, left_distance)),
                    Some((right_version, right_range, right_distance)),
                ) => {
                    if left_distance < right_distance {
                        (left_version, left_range, true)
                    } else {
                        (right_version, right_range, false)
                    }
                }
                // This shouldn't happen as the first thing we do when we initialize the cache
                // is put the first and last blocks into it
                _ => unreachable!("There should always be a block in the cache!"),
            }
        };

        // Go through the blocks, and add them into the cache
        let mut running_version = start_version;
        for _ in range {
            let info = self.add_block(running_version).await?;

            // Increment to the next block
            if traverse_right {
                running_version = info.end_version.saturating_add(1);
            } else {
                running_version = info.start_version.saturating_sub(1);
            }
        }

        if let Some(info) = self.blocks.read().unwrap().get(&block_index) {
            Ok(*info)
        } else {
            // If for some reason the block doesn't get found, retry with block incomplete
            Err(ApiError::BlockIncomplete)
        }
    }

    /// Retrieve block info, and add it to the index
    async fn add_block(&self, block_version: u64) -> ApiResult<BlockInfo> {
        let info_response = self.rest_client.get_block_info(block_version).await?;
        let info = info_response.into_inner();

        // Add into the cache (keeping the write lock short)
        let info = {
            let mut map = self.blocks.write().unwrap();
            map.insert(info.block_height, info);
            info
        };

        // Write hash to index mapping
        {
            let mut map = self.hashes.write().unwrap();
            map.insert(info.block_hash, info.block_height);
        }

        // Write version to index mapping
        {
            let mut map = self.versions.write().unwrap();
            // The only versions that will be used are the start and end versions of a block
            // As those are the only versions that will be returned by ledger info
            map.insert(info.start_version, info.block_height);
            map.insert(info.end_version, info.block_height);
        }
        Ok(info)
    }

    /// Retrieve the block index for the version
    /// TODO: We can search through the versions to find the closest version
    pub async fn get_block_info_by_version(&self, version: u64) -> ApiResult<BlockInfo> {
        // If we already have the version, let's roll with it
        let maybe_index = { self.versions.read().unwrap().get(&version).copied() };

        if let Some(index) = maybe_index {
            if let Some(info) = self.blocks.read().unwrap().get(&index) {
                return Ok(*info);
            }
        }

        // Lookup block info by version
        self.add_block(version).await
    }

    /// Retrieve the block info for the hash
    ///
    /// This is particularly bad, since there's no index on this value.  It can only be derived
    /// from the cache, otherwise it needs to fail immediately.  This cache will need to be saved
    /// somewhere for these purposes.
    ///
    /// We could use the BlockMetadata transaction's hash rather than the block hash as a hack,
    /// and that is always indexed
    ///
    /// TODO: Improve reliability
    pub async fn get_block_index_by_hash(&self, hash: &HashValue) -> ApiResult<u64> {
        if let Some(version) = self.hashes.read().unwrap().get(hash) {
            Ok(*version)
        } else {
            // If for some reason the block doesn't get found, retry with block incomplete
            Err(ApiError::BlockIncomplete)
        }
    }
}
<|endoftext|>


<commit_before>use std::hash::Hash;
use super::hash_tables::SeparateChainingHashST;

/// Mathematical set. A collection of distinct keys
pub trait Set<K> {
    /// create an empty set
    fn new() -> Self;
    /// add the key to the set
    fn add(&mut self, key: K);
    /// remove the key from the set
    fn remove(&mut self, key: &K);
    /// return the number of keys in the set
    fn size(&self) -> usize;
    // iterator through keys in the set
    // fn iter(&self) -> Interator<&K>
}

type HashSet<K> = SeparateChainingHashST<K,()>;

impl<K: Hash + PartialEq> Set<K> for HashSet<K> {
    fn new() -> Self {
        Self::new()
    }

    fn add(&mut self, key: K) {
        self.put(key, ());
    }

    fn remove(&mut self, key: &K) {
        self.delete(key);
    }

    fn size(&self) -> usize {
        self.size()
    }

}

#[test]
fn test_hash_set() {
    let mut s = HashSet::new();
    assert_eq!(s.size(), 0);
    s.add(100);
    s.add(100);
    s.add(200);
    assert_eq!(s.size(), 2);
    s.remove(&100);
    assert_eq!(s.size(), 1);
}
<commit_msg>searching_application: make HashSet public<commit_after>use std::hash::Hash;
use super::hash_tables::SeparateChainingHashST;

/// Mathematical set. A collection of distinct keys
pub trait Set<K> {
    /// create an empty set
    fn new() -> Self;
    /// add the key to the set
    fn add(&mut self, key: K);
    /// remove the key from the set
    fn remove(&mut self, key: &K);
    /// return the number of keys in the set
    fn size(&self) -> usize;
    // iterator through keys in the set
    // fn iter(&self) -> Interator<&K>
}

pub type HashSet<K> = SeparateChainingHashST<K,()>;

impl<K: Hash + PartialEq> Set<K> for HashSet<K> {
    fn new() -> Self {
        Self::new()
    }

    fn add(&mut self, key: K) {
        self.put(key, ());
    }

    fn remove(&mut self, key: &K) {
        self.delete(key);
    }

    fn size(&self) -> usize {
        self.size()
    }

}

#[test]
fn test_hash_set() {
    let mut s = HashSet::new();
    assert_eq!(s.size(), 0);
    s.add(100);
    s.add(100);
    s.add(200);
    assert_eq!(s.size(), 2);
    s.remove(&100);
    assert_eq!(s.size(), 1);
}
<|endoftext|>