<commit_before>//! A higher level attributes based on TokenTree, with also some shortcuts.

use std::{ops, sync::Arc};

use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
use either::Either;
use hir_expand::{hygiene::Hygiene, AstId, InFile};
use itertools::Itertools;
use mbe::ast_to_token_tree;
use syntax::{
    ast::{self, AstNode, AttrsOwner},
    match_ast, AstToken, SmolStr, SyntaxNode,
};
use tt::Subtree;

use crate::{
    db::DefDatabase,
    item_tree::{ItemTreeId, ItemTreeNode},
    nameres::ModuleSource,
    path::ModPath,
    src::HasChildSource,
    AdtId, AttrDefId, Lookup,
};

/// Holds documentation
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Documentation(String);

impl Documentation {
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl From<Documentation> for String {
    fn from(Documentation(string): Documentation) -> Self {
        string
    }
}

/// Syntactical attributes, without filtering of `cfg_attr`s.
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct RawAttrs {
    entries: Option<Arc<[Attr]>>,
}

#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct Attrs(RawAttrs);

impl ops::Deref for RawAttrs {
    type Target = [Attr];

    fn deref(&self) -> &[Attr] {
        match &self.entries {
            Some(it) => &*it,
            None => &[],
        }
    }
}

impl ops::Deref for Attrs {
    type Target = [Attr];

    fn deref(&self) -> &[Attr] {
        match &self.0.entries {
            Some(it) => &*it,
            None => &[],
        }
    }
}

impl RawAttrs {
    pub const EMPTY: Self = Self { entries: None };

    pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Self {
        let (inner_attrs, inner_docs) = inner_attributes(owner.syntax())
            .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs))));

        let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none());
        let attrs = outer_attrs
            .chain(inner_attrs.into_iter().flatten())
            .map(|attr| (attr.syntax().text_range().start(), Attr::from_src(attr, hygiene)));

        let outer_docs =
            ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer);
        let docs = outer_docs.chain(inner_docs.into_iter().flatten()).map(|docs_text| {
            (
                docs_text.syntax().text_range().start(),
                docs_text.doc_comment().map(|doc| Attr {
                    input: Some(AttrInput::Literal(SmolStr::new(doc))),
                    path: ModPath::from(hir_expand::name!(doc)),
                }),
            )
        });
        // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
        let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect();
        let entries = if attrs.is_empty() {
            // Avoid heap allocation
            None
        } else {
            Some(attrs.into_iter().flat_map(|(_, attr)| attr).collect())
        };
        Self { entries }
    }

    fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Self {
        let hygiene = Hygiene::new(db.upcast(), owner.file_id);
        Self::new(owner.value, &hygiene)
    }

    pub(crate) fn merge(&self, other: Self) -> Self {
        match (&self.entries, &other.entries) {
            (None, None) => Self::EMPTY,
            (Some(entries), None) | (None, Some(entries)) => {
                Self { entries: Some(entries.clone()) }
            }
            (Some(a), Some(b)) => {
                Self { entries: Some(a.iter().chain(b.iter()).cloned().collect()) }
            }
        }
    }

    /// Processes `cfg_attr`s, returning the resulting semantic `Attrs`.
    pub(crate) fn filter(self, db: &dyn DefDatabase, krate: CrateId) -> Attrs {
        let has_cfg_attrs = self.iter().any(|attr| {
            attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr])
        });
        if !has_cfg_attrs {
            return Attrs(self);
        }

        let crate_graph = db.crate_graph();
        let new_attrs = self
            .iter()
            .filter_map(|attr| {
                let attr = attr.clone();
                let is_cfg_attr =
                    attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr]);
                if !is_cfg_attr {
                    return Some(attr);
                }

                let subtree = match &attr.input {
                    Some(AttrInput::TokenTree(it)) => it,
                    _ => return Some(attr),
                };

                // Input subtree is: `(cfg, attr)`
                // Split it up into a `cfg` and an `attr` subtree.
                // FIXME: There should be a common API for this.
                let mut saw_comma = false;
                let (mut cfg, attr): (Vec<_>, Vec<_>) =
                    subtree.clone().token_trees.into_iter().partition(|tree| {
                        if saw_comma {
                            return false;
                        }

                        match tree {
                            tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => {
                                saw_comma = true;
                            }
                            _ => {}
                        }

                        true
                    });
                cfg.pop(); // `,` ends up in here

                let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg };
                let cfg = CfgExpr::parse(&cfg);

                let cfg_options = &crate_graph[krate].cfg_options;
                if cfg_options.check(&cfg) == Some(false) {
                    None
                } else {
                    let attr = Subtree { delimiter: None, token_trees: attr };
                    let attr = ast::Attr::parse(&attr.to_string()).ok()?;
                    let hygiene = Hygiene::new_unhygienic(); // FIXME
                    Attr::from_src(attr, &hygiene)
                }
            })
            .collect();

        Attrs(RawAttrs { entries: Some(new_attrs) })
    }
}

impl Attrs {
    pub const EMPTY: Self = Self(RawAttrs::EMPTY);

    pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
        let raw_attrs = match def {
            AttrDefId::ModuleId(module) => {
                let def_map = db.crate_def_map(module.krate);
                let mod_data = &def_map[module.local_id];
                match mod_data.declaration_source(db) {
                    Some(it) => {
                        RawAttrs::from_attrs_owner(db, it.as_ref().map(|it| it as &dyn AttrsOwner))
                    }
                    None => RawAttrs::from_attrs_owner(
                        db,
                        mod_data.definition_source(db).as_ref().map(|src| match src {
                            ModuleSource::SourceFile(file) => file as &dyn AttrsOwner,
                            ModuleSource::Module(module) => module as &dyn AttrsOwner,
                        }),
                    ),
                }
            }
            AttrDefId::FieldId(it) => {
                let src = it.parent.child_source(db);
                match &src.value[it.local_id] {
                    Either::Left(_tuple) => RawAttrs::default(),
                    Either::Right(record) => RawAttrs::from_attrs_owner(db, src.with_value(record)),
                }
            }
            AttrDefId::EnumVariantId(var_id) => {
                let src = var_id.parent.child_source(db);
                let src = src.as_ref().map(|it| &it[var_id.local_id]);
                RawAttrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner))
            }
            AttrDefId::AdtId(it) => match it {
                AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db),
                AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db),
                AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
            },
            AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db),
            AttrDefId::MacroDefId(it) => {
                it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db))
            }
            AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db),
            AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db),
            AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
            AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
            AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
        };

        raw_attrs.filter(db, def.krate(db))
    }

    pub fn merge(&self, other: Attrs) -> Attrs {
        match (&self.0.entries, &other.0.entries) {
            (None, None) => Attrs::EMPTY,
            (Some(entries), None) | (None, Some(entries)) => {
                Attrs(RawAttrs { entries: Some(entries.clone()) })
            }
            (Some(a), Some(b)) => {
                Attrs(RawAttrs { entries: Some(a.iter().chain(b.iter()).cloned().collect()) })
            }
        }
    }

    pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
        AttrQuery { attrs: self, key }
    }

    pub fn cfg(&self) -> Option<CfgExpr> {
        // FIXME: handle cfg_attr :-)
        let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse).collect::<Vec<_>>();
        match cfgs.len() {
            0 => None,
            1 => Some(cfgs.pop().unwrap()),
            _ => Some(CfgExpr::All(cfgs)),
        }
    }
    pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
        match self.cfg() {
            None => true,
            Some(cfg) => cfg_options.check(&cfg) != Some(false),
        }
    }

    pub fn docs(&self) -> Option<Documentation> {
        let docs = self
            .by_key("doc")
            .attrs()
            .flat_map(|attr| match attr.input.as_ref()? {
                AttrInput::Literal(s) => Some(s),
                AttrInput::TokenTree(_) => None,
            })
            .intersperse(&SmolStr::new_inline("\n"))
            .map(|it| it.as_str())
            .collect::<String>();
        if docs.is_empty() {
            None
        } else {
            Some(Documentation(docs.into()))
        }
    }
}

fn inner_attributes(
    syntax: &SyntaxNode,
) -> Option<(impl Iterator<Item = ast::Attr>, impl Iterator<Item = ast::Comment>)> {
    let (attrs, docs) = match_ast! {
        match syntax {
            ast::SourceFile(it) => (it.attrs(), ast::CommentIter::from_syntax_node(it.syntax())),
            ast::ExternBlock(it) => {
                let extern_item_list = it.extern_item_list()?;
                (extern_item_list.attrs(), ast::CommentIter::from_syntax_node(extern_item_list.syntax()))
            },
            ast::Fn(it) => {
                let body = it.body()?;
                (body.attrs(), ast::CommentIter::from_syntax_node(body.syntax()))
            },
            ast::Impl(it) => {
                let assoc_item_list = it.assoc_item_list()?;
                (assoc_item_list.attrs(), ast::CommentIter::from_syntax_node(assoc_item_list.syntax()))
            },
            ast::Module(it) => {
                let item_list = it.item_list()?;
                (item_list.attrs(), ast::CommentIter::from_syntax_node(item_list.syntax()))
            },
            // FIXME: BlockExpr's only accept inner attributes in specific cases
            // Excerpt from the reference:
            // Block expressions accept outer and inner attributes, but only when they are the outer
            // expression of an expression statement or the final expression of another block expression.
            ast::BlockExpr(it) => return None,
            _ => return None,
        }
    };
    let attrs = attrs.filter(|attr| attr.excl_token().is_some());
    let docs = docs.filter(|doc| doc.is_inner());
    Some((attrs, docs))
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Attr {
    pub(crate) path: ModPath,
    pub(crate) input: Option<AttrInput>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AttrInput {
    /// `#[attr = "string"]`
    Literal(SmolStr),
    /// `#[attr(subtree)]`
    TokenTree(Subtree),
}

impl Attr {
    fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> {
        let path = ModPath::from_src(ast.path()?, hygiene)?;
        let input = if let Some(lit) = ast.literal() {
            let value = match lit.kind() {
                ast::LiteralKind::String(string) => string.value()?.into(),
                _ => lit.syntax().first_token()?.text().trim_matches('"').into(),
            };
            Some(AttrInput::Literal(value))
        } else if let Some(tt) = ast.token_tree() {
            Some(AttrInput::TokenTree(ast_to_token_tree(&tt)?.0))
        } else {
            None
        };
        Some(Attr { path, input })
    }
}

#[derive(Debug, Clone, Copy)]
pub struct AttrQuery<'a> {
    attrs: &'a Attrs,
    key: &'static str,
}

impl<'a> AttrQuery<'a> {
    pub fn tt_values(self) -> impl Iterator<Item = &'a Subtree> {
        self.attrs().filter_map(|attr| match attr.input.as_ref()? {
            AttrInput::TokenTree(it) => Some(it),
            _ => None,
        })
    }

    pub fn string_value(self) -> Option<&'a SmolStr> {
        self.attrs().find_map(|attr| match attr.input.as_ref()? {
            AttrInput::Literal(it) => Some(it),
            _ => None,
        })
    }

    pub fn exists(self) -> bool {
        self.attrs().next().is_some()
    }

    fn attrs(self) -> impl Iterator<Item = &'a Attr> {
        let key = self.key;
        self.attrs
            .iter()
            .filter(move |attr| attr.path.as_ident().map_or(false, |s| s.to_string() == key))
    }
}

fn attrs_from_ast<N>(src: AstId<N>, db: &dyn DefDatabase) -> RawAttrs
where
    N: ast::AttrsOwner,
{
    let src = InFile::new(src.file_id, src.to_node(db.upcast()));
    RawAttrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
}

fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs {
    let tree = db.item_tree(id.file_id);
    let mod_item = N::id_to_mod_item(id.value);
    tree.raw_attrs(mod_item.into()).clone()
}
<commit_msg>Fix parsing of active cfg_attr<commit_after>//! A higher level attributes based on TokenTree, with also some shortcuts.

use std::{ops, sync::Arc};

use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
use either::Either;
use hir_expand::{hygiene::Hygiene, AstId, InFile};
use itertools::Itertools;
use mbe::ast_to_token_tree;
use syntax::{
    ast::{self, AstNode, AttrsOwner},
    match_ast, AstToken, SmolStr, SyntaxNode,
};
use tt::Subtree;

use crate::{
    db::DefDatabase,
    item_tree::{ItemTreeId, ItemTreeNode},
    nameres::ModuleSource,
    path::ModPath,
    src::HasChildSource,
    AdtId, AttrDefId, Lookup,
};

/// Holds documentation
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Documentation(String);

impl Documentation {
    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl From<Documentation> for String {
    fn from(Documentation(string): Documentation) -> Self {
        string
    }
}

/// Syntactical attributes, without filtering of `cfg_attr`s.
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct RawAttrs {
    entries: Option<Arc<[Attr]>>,
}

#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct Attrs(RawAttrs);

impl ops::Deref for RawAttrs {
    type Target = [Attr];

    fn deref(&self) -> &[Attr] {
        match &self.entries {
            Some(it) => &*it,
            None => &[],
        }
    }
}

impl ops::Deref for Attrs {
    type Target = [Attr];

    fn deref(&self) -> &[Attr] {
        match &self.0.entries {
            Some(it) => &*it,
            None => &[],
        }
    }
}

impl RawAttrs {
    pub const EMPTY: Self = Self { entries: None };

    pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Self {
        let (inner_attrs, inner_docs) = inner_attributes(owner.syntax())
            .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs))));

        let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none());
        let attrs = outer_attrs
            .chain(inner_attrs.into_iter().flatten())
            .map(|attr| (attr.syntax().text_range().start(), Attr::from_src(attr, hygiene)));

        let outer_docs =
            ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer);
        let docs = outer_docs.chain(inner_docs.into_iter().flatten()).map(|docs_text| {
            (
                docs_text.syntax().text_range().start(),
                docs_text.doc_comment().map(|doc| Attr {
                    input: Some(AttrInput::Literal(SmolStr::new(doc))),
                    path: ModPath::from(hir_expand::name!(doc)),
                }),
            )
        });
        // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
        let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect();
        let entries = if attrs.is_empty() {
            // Avoid heap allocation
            None
        } else {
            Some(attrs.into_iter().flat_map(|(_, attr)| attr).collect())
        };
        Self { entries }
    }

    fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Self {
        let hygiene = Hygiene::new(db.upcast(), owner.file_id);
        Self::new(owner.value, &hygiene)
    }

    pub(crate) fn merge(&self, other: Self) -> Self {
        match (&self.entries, &other.entries) {
            (None, None) => Self::EMPTY,
            (Some(entries), None) | (None, Some(entries)) => {
                Self { entries: Some(entries.clone()) }
            }
            (Some(a), Some(b)) => {
                Self { entries: Some(a.iter().chain(b.iter()).cloned().collect()) }
            }
        }
    }

    /// Processes `cfg_attr`s, returning the resulting semantic `Attrs`.
    pub(crate) fn filter(self, db: &dyn DefDatabase, krate: CrateId) -> Attrs {
        let has_cfg_attrs = self.iter().any(|attr| {
            attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr])
        });
        if !has_cfg_attrs {
            return Attrs(self);
        }

        let crate_graph = db.crate_graph();
        let new_attrs = self
            .iter()
            .filter_map(|attr| {
                let attr = attr.clone();
                let is_cfg_attr =
                    attr.path.as_ident().map_or(false, |name| *name == hir_expand::name![cfg_attr]);
                if !is_cfg_attr {
                    return Some(attr);
                }

                let subtree = match &attr.input {
                    Some(AttrInput::TokenTree(it)) => it,
                    _ => return Some(attr),
                };

                // Input subtree is: `(cfg, attr)`
                // Split it up into a `cfg` and an `attr` subtree.
                // FIXME: There should be a common API for this.
                let mut saw_comma = false;
                let (mut cfg, attr): (Vec<_>, Vec<_>) =
                    subtree.clone().token_trees.into_iter().partition(|tree| {
                        if saw_comma {
                            return false;
                        }

                        match tree {
                            tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => {
                                saw_comma = true;
                            }
                            _ => {}
                        }

                        true
                    });
                cfg.pop(); // `,` ends up in here

                let attr = Subtree { delimiter: None, token_trees: attr };
                let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg };
                let cfg = CfgExpr::parse(&cfg);

                let cfg_options = &crate_graph[krate].cfg_options;
                if cfg_options.check(&cfg) == Some(false) {
                    None
                } else {
                    let attr = ast::Attr::parse(&format!("#[{}]", attr)).ok()?;
                    let hygiene = Hygiene::new_unhygienic(); // FIXME
                    Attr::from_src(attr, &hygiene)
                }
            })
            .collect();

        Attrs(RawAttrs { entries: Some(new_attrs) })
    }
}

impl Attrs {
    pub const EMPTY: Self = Self(RawAttrs::EMPTY);

    pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
        let raw_attrs = match def {
            AttrDefId::ModuleId(module) => {
                let def_map = db.crate_def_map(module.krate);
                let mod_data = &def_map[module.local_id];
                match mod_data.declaration_source(db) {
                    Some(it) => {
                        RawAttrs::from_attrs_owner(db, it.as_ref().map(|it| it as &dyn AttrsOwner))
                    }
                    None => RawAttrs::from_attrs_owner(
                        db,
                        mod_data.definition_source(db).as_ref().map(|src| match src {
                            ModuleSource::SourceFile(file) => file as &dyn AttrsOwner,
                            ModuleSource::Module(module) => module as &dyn AttrsOwner,
                        }),
                    ),
                }
            }
            AttrDefId::FieldId(it) => {
                let src = it.parent.child_source(db);
                match &src.value[it.local_id] {
                    Either::Left(_tuple) => RawAttrs::default(),
                    Either::Right(record) => RawAttrs::from_attrs_owner(db, src.with_value(record)),
                }
            }
            AttrDefId::EnumVariantId(var_id) => {
                let src = var_id.parent.child_source(db);
                let src = src.as_ref().map(|it| &it[var_id.local_id]);
                RawAttrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner))
            }
            AttrDefId::AdtId(it) => match it {
                AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db),
                AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db),
                AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
            },
            AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db),
            AttrDefId::MacroDefId(it) => {
                it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db))
            }
            AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db),
            AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db),
            AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
            AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
            AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
        };

        raw_attrs.filter(db, def.krate(db))
    }

    pub fn merge(&self, other: Attrs) -> Attrs {
        match (&self.0.entries, &other.0.entries) {
            (None, None) => Attrs::EMPTY,
            (Some(entries), None) | (None, Some(entries)) => {
                Attrs(RawAttrs { entries: Some(entries.clone()) })
            }
            (Some(a), Some(b)) => {
                Attrs(RawAttrs { entries: Some(a.iter().chain(b.iter()).cloned().collect()) })
            }
        }
    }

    pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
        AttrQuery { attrs: self, key }
    }

    pub fn cfg(&self) -> Option<CfgExpr> {
        // FIXME: handle cfg_attr :-)
        let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse).collect::<Vec<_>>();
        match cfgs.len() {
            0 => None,
            1 => Some(cfgs.pop().unwrap()),
            _ => Some(CfgExpr::All(cfgs)),
        }
    }
    pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
        match self.cfg() {
            None => true,
            Some(cfg) => cfg_options.check(&cfg) != Some(false),
        }
    }

    pub fn docs(&self) -> Option<Documentation> {
        let docs = self
            .by_key("doc")
            .attrs()
            .flat_map(|attr| match attr.input.as_ref()? {
                AttrInput::Literal(s) => Some(s),
                AttrInput::TokenTree(_) => None,
            })
            .intersperse(&SmolStr::new_inline("\n"))
            .map(|it| it.as_str())
            .collect::<String>();
        if docs.is_empty() {
            None
        } else {
            Some(Documentation(docs.into()))
        }
    }
}

fn inner_attributes(
    syntax: &SyntaxNode,
) -> Option<(impl Iterator<Item = ast::Attr>, impl Iterator<Item = ast::Comment>)> {
    let (attrs, docs) = match_ast! {
        match syntax {
            ast::SourceFile(it) => (it.attrs(), ast::CommentIter::from_syntax_node(it.syntax())),
            ast::ExternBlock(it) => {
                let extern_item_list = it.extern_item_list()?;
                (extern_item_list.attrs(), ast::CommentIter::from_syntax_node(extern_item_list.syntax()))
            },
            ast::Fn(it) => {
                let body = it.body()?;
                (body.attrs(), ast::CommentIter::from_syntax_node(body.syntax()))
            },
            ast::Impl(it) => {
                let assoc_item_list = it.assoc_item_list()?;
                (assoc_item_list.attrs(), ast::CommentIter::from_syntax_node(assoc_item_list.syntax()))
            },
            ast::Module(it) => {
                let item_list = it.item_list()?;
                (item_list.attrs(), ast::CommentIter::from_syntax_node(item_list.syntax()))
            },
            // FIXME: BlockExpr's only accept inner attributes in specific cases
            // Excerpt from the reference:
            // Block expressions accept outer and inner attributes, but only when they are the outer
            // expression of an expression statement or the final expression of another block expression.
            ast::BlockExpr(it) => return None,
            _ => return None,
        }
    };
    let attrs = attrs.filter(|attr| attr.excl_token().is_some());
    let docs = docs.filter(|doc| doc.is_inner());
    Some((attrs, docs))
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Attr {
    pub(crate) path: ModPath,
    pub(crate) input: Option<AttrInput>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AttrInput {
    /// `#[attr = "string"]`
    Literal(SmolStr),
    /// `#[attr(subtree)]`
    TokenTree(Subtree),
}

impl Attr {
    fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> {
        let path = ModPath::from_src(ast.path()?, hygiene)?;
        let input = if let Some(lit) = ast.literal() {
            let value = match lit.kind() {
                ast::LiteralKind::String(string) => string.value()?.into(),
                _ => lit.syntax().first_token()?.text().trim_matches('"').into(),
            };
            Some(AttrInput::Literal(value))
        } else if let Some(tt) = ast.token_tree() {
            Some(AttrInput::TokenTree(ast_to_token_tree(&tt)?.0))
        } else {
            None
        };
        Some(Attr { path, input })
    }
}

#[derive(Debug, Clone, Copy)]
pub struct AttrQuery<'a> {
    attrs: &'a Attrs,
    key: &'static str,
}

impl<'a> AttrQuery<'a> {
    pub fn tt_values(self) -> impl Iterator<Item = &'a Subtree> {
        self.attrs().filter_map(|attr| match attr.input.as_ref()? {
            AttrInput::TokenTree(it) => Some(it),
            _ => None,
        })
    }

    pub fn string_value(self) -> Option<&'a SmolStr> {
        self.attrs().find_map(|attr| match attr.input.as_ref()? {
            AttrInput::Literal(it) => Some(it),
            _ => None,
        })
    }

    pub fn exists(self) -> bool {
        self.attrs().next().is_some()
    }

    fn attrs(self) -> impl Iterator<Item = &'a Attr> {
        let key = self.key;
        self.attrs
            .iter()
            .filter(move |attr| attr.path.as_ident().map_or(false, |s| s.to_string() == key))
    }
}

fn attrs_from_ast<N>(src: AstId<N>, db: &dyn DefDatabase) -> RawAttrs
where
    N: ast::AttrsOwner,
{
    let src = InFile::new(src.file_id, src.to_node(db.upcast()));
    RawAttrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
}

fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs {
    let tree = db.item_tree(id.file_id);
    let mod_item = N::id_to_mod_item(id.value);
    tree.raw_attrs(mod_item.into()).clone()
}
<|endoftext|>


<commit_before>extern crate xml;

use std::ascii::AsciiExt;
use std::default::Default;
use std::io;
use xml::{Element, ElementBuilder, Parser, Xml};

trait ElementUtils {
    fn tag_with_text(&mut self, child_name: &'static str, child_body: &str);
    fn tag_with_text_opt(&mut self, child_name: &'static str, child_body: &Option<String>);
}

impl ElementUtils for Element {
    fn tag_with_text(&mut self, child_name: &'static str, child_body: &str) {
        self.tag(elem_with_text(child_name, child_body));
    }

    fn tag_with_text_opt(&mut self, child_name: &'static str, child_body: &Option<String>) {
        if let Some(ref c) = *child_body {
            self.tag_with_text(child_name, &c);
        }
    }
}

fn elem_with_text(tag_name: &'static str, chars: &str) -> Element {
    let mut elem = Element::new(tag_name.to_string(), None, vec![]);
    elem.text(chars.to_string());
    elem
}

trait ViaXml {
    fn to_xml(&self) -> Element;
    fn from_xml(element: Element) -> Result<Self, &'static str>;
}

/// RSS
///
/// "At the top level, a RSS document is a \<rss\> element, with a mandatory attribute called
/// version, that specifies the version of RSS that the document conforms to. If it conforms to
/// this specification, the version attribute must be 2.0."
///
/// [RSS 2.0 Specification § RSS]
/// (http://cyber.law.harvard.edu/rss/rss.html#whatIsRss)
#[derive(Default)]
pub struct Rss(pub Channel);

impl ViaXml for Rss {
    fn to_xml(&self) -> Element {
        let mut rss = Element::new("rss".to_string(), None, vec![("version".to_string(), None, "2.0".to_string())]);

        let &Rss(ref channel) = self;
        rss.tag(channel.to_xml());

        rss
    }

    fn from_xml(rss_elem: Element) -> Result<Self, &'static str> {
        if rss_elem.name.to_ascii_lowercase() != "rss" {
            panic!("Expected <rss>, found <{}>", rss_elem.name);
        }

        let channel_elem = match rss_elem.get_child("channel", None) {
            Some(elem) => elem,
            None => return Err("No <channel> element found in <rss>"),
        };

        let channel = try!(ViaXml::from_xml(channel_elem.clone()));

        Ok(Rss(channel))
    }
}

impl Rss {
    pub fn to_string(&self) -> String {
        let mut ret = format!("{}", Xml::PINode("xml version='1.0' encoding='UTF-8'".to_string()));
        ret.push_str(&format!("{}", self.to_xml()));
        ret
    }

    pub fn from_read(reader: &mut io::Read) -> Result<Self, &'static str> {
        let mut rss_string = String::new();

        if let Err(..) = reader.read_to_string(&mut rss_string) {
            return Err("Error reading string from reader");
        }

        let mut parser = Parser::new();
        parser.feed_str(&rss_string);

        let mut builder = ElementBuilder::new();

        for event in parser {
            if let Some(Ok(element)) = builder.handle_event(event) {
                return ViaXml::from_xml(element);
            }
        }

        Err("RSS read error")
    }
}

/// Channel
///
/// "Subordinate to the \<rss\> element is a single \<channel\> element, which contains information
/// about the channel (metadata) and its contents."
///
/// [RSS 2.0 Specification § Channel]
/// (http://cyber.law.harvard.edu/rss/rss.html#requiredChannelElements)
///
/// ## Examples
///
/// ```
/// use rss::Channel;
/// use std::default::Default;
///
/// let channel = Channel {
///     title: "My Blog".to_string(),
///     link: "http://myblog.com".to_string(),
///     description: "Where I write stuff".to_string(),
///     items: vec![],
///     ..Default::default()
/// };
/// ```
#[derive(Default)]
pub struct Channel {
    pub title: String,
    pub link: String,
    pub description: String,
    pub items: Vec<Item>,
    pub language: Option<String>,
    pub copyright: Option<String>,
    pub managing_editor: Option<String>,
    pub web_master: Option<String>,
    pub pub_date: Option<String>,
    pub last_build_date: Option<String>,
    pub categories: Vec<Category>,
    pub generator: Option<String>,
    pub docs: Option<String>,
    // pub cloud:
    // pub ttl:
    pub image: Option<String>,
    pub rating: Option<String>,
    // pub text_input:
    pub skip_hours: Option<String>,
    pub skip_days: Option<String>,
}

impl ViaXml for Channel {
    fn to_xml(&self) -> Element {
        let mut channel = Element::new("channel".to_string(), None, vec![]);

        channel.tag_with_text("title", &self.title);
        channel.tag_with_text("link", &self.link);
        channel.tag_with_text("description", &self.description);

        for item in &self.items {
            channel.tag(item.to_xml());
        }

        channel.tag_with_text_opt("language", &self.language);
        channel.tag_with_text_opt("copyright", &self.copyright);
        channel.tag_with_text_opt("managingEditor", &self.managing_editor);
        channel.tag_with_text_opt("webMaster", &self.web_master);
        channel.tag_with_text_opt("pubDate", &self.pub_date);
        channel.tag_with_text_opt("lastBuildDate", &self.last_build_date);
        channel.tag_with_text_opt("generator", &self.generator);
        channel.tag_with_text_opt("docs", &self.docs);
        channel.tag_with_text_opt("image", &self.image);
        channel.tag_with_text_opt("rating", &self.rating);
        channel.tag_with_text_opt("skipHours", &self.skip_hours);
        channel.tag_with_text_opt("skipDays", &self.skip_days);

        for category in &self.categories {
            channel.tag(category.to_xml());
        }

        channel
    }

    fn from_xml(element: Element) -> Result<Self, &'static str> {
        let title = match element.get_child("title", None) {
            Some(element) => element.content_str(),
            None => return Err("<channel> is missing required <title> element"),
        };

        let link = match element.get_child("link", None) {
            Some(element) => element.content_str(),
            None => return Err("<channel> is missing required <link> element"),
        };

        let description = match element.get_child("description", None) {
            Some(element) => element.content_str(),
            None => return Err("<channel> is missing required <description> element"),
        };

        let items = element.get_children("item", None)
            .map(|e| ViaXml::from_xml(e.clone()).unwrap())
            .collect();

        let categories = element.get_children("category", None)
            .map(|e| ViaXml::from_xml(e.clone()).unwrap())
            .collect();

        Ok(Channel {
            title: title,
            link: link,
            description: description,
            items: items,
            categories: categories,
            ..Default::default()  // TODO
        })
    }
}

/// Item
///
/// [RSS 2.0 Specification § Item]
/// http://cyber.law.harvard.edu/rss/rss.html#hrelementsOfLtitemgt
#[derive(Default)]
pub struct Item {
    pub title: Option<String>,
    pub link: Option<String>,
    pub description: Option<String>,
    pub author: Option<String>,
    pub categories: Vec<Category>,
    // pub comments
    // pub enclosure
    // pub guid
    // pub pubDate
    // pub source
}

impl ViaXml for Item {
    fn to_xml(&self) -> Element {
        let mut item = Element::new("item".to_string(), None, vec![]);

        item.tag_with_text_opt("title", &self.title);
        item.tag_with_text_opt("link", &self.link);
        item.tag_with_text_opt("description", &self.description);
        item.tag_with_text_opt("author", &self.author);

        for category in &self.categories {
            item.tag(category.to_xml());
        }

        item
    }

    fn from_xml(element: Element) -> Result<Self, &'static str> {
        let title = element.get_child("title", None).map(Element::content_str);
        let link = element.get_child("link", None).map(Element::content_str);
        let description = element.get_child("description", None).map(Element::content_str);
        let author = element.get_child("author", None).map(Element::content_str);

        let categories = element.get_children("category", None)
            .map(|e| ViaXml::from_xml(e.clone()).unwrap())
            .collect();

        Ok(Item {
            title: title,
            link: link,
            description: description,
            categories: categories,
            author: author,
        })
    }
}

/// Category
///
/// [RSS 2.0 Specification § Category]
/// (http://cyber.law.harvard.edu/rss/rss.html#ltcategorygtSubelementOfLtitemgt)
#[derive(Default)]
pub struct Category {
    pub domain: Option<String>,
    pub value: String,
}

impl ViaXml for Category {
    fn to_xml(&self) -> Element {
        let mut category = match self.domain {
            Some(ref d) => Element::new("category".to_string(), None, vec![("domain".to_string(), None, d.clone())]),
            None => Element::new("category".to_string(), None, vec![]),
        };
        category.text(self.value.clone());
        category
    }

    fn from_xml(elem: Element) -> Result<Self, &'static str> {
        let domain = elem.get_attribute("domain", None).map(|s| s.to_string());
        let value = elem.content_str();

        Ok(Category {
            domain: domain,
            value: value,
        })
    }
}

#[cfg(test)]
mod test {
    use std::default::Default;
    use std::fs::File;
    use super::{Rss, Item, Channel};

    #[test]
    fn test_basic_to_string() {
        let item = Item {
            title: Some("My first post!".to_string()),
            link: Some("http://myblog.com/post1".to_string()),
            description: Some("This is my first post".to_string()),
            ..Default::default()
        };

        let channel = Channel {
            title: "My Blog".to_string(),
            link: "http://myblog.com".to_string(),
            description: "Where I write stuff".to_string(),
            items: vec![item],
            ..Default::default()
        };

        let rss = Rss(channel);
        assert_eq!(rss.to_string(), "<?xml version=\'1.0\' encoding=\'UTF-8\'?><rss version=\'2.0\'><channel><title>My Blog</title><link>http://myblog.com</link><description>Where I write stuff</description><item><title>My first post!</title><link>http://myblog.com/post1</link><description>This is my first post</description></item></channel></rss>");
    }

    #[test]
    fn test_from_file() {
        let mut file = File::open("test-data/pinboard.xml").unwrap();
        let rss = Rss::from_read(&mut file).unwrap();
        assert!(rss.to_string().len() > 0);
    }

    #[test]
    #[should_panic]
    fn test_from_read_no_channels() {
        let mut rss_bytes = "<rss></rss>".as_bytes();
        let Rss(_) = Rss::from_read(&mut rss_bytes).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_from_read_one_channel_no_properties() {
        let mut rss_bytes = "<rss><channel></channel></rss>".as_bytes();
        let Rss(_) = Rss::from_read(&mut rss_bytes).unwrap();
    }

    #[test]
    fn test_from_read_one_channel() {
        let mut rss_bytes = "<rss><channel><title>Hello world!</title><description></description><link></link></channel></rss>".as_bytes();
        let Rss(channel) = Rss::from_read(&mut rss_bytes).unwrap();
        assert_eq!("Hello world!", channel.title);
    }
}
<commit_msg>Support <comments> within <item><commit_after>extern crate xml;

use std::ascii::AsciiExt;
use std::default::Default;
use std::io;
use xml::{Element, ElementBuilder, Parser, Xml};

trait ElementUtils {
    fn tag_with_text(&mut self, child_name: &'static str, child_body: &str);
    fn tag_with_text_opt(&mut self, child_name: &'static str, child_body: &Option<String>);
}

impl ElementUtils for Element {
    fn tag_with_text(&mut self, child_name: &'static str, child_body: &str) {
        self.tag(elem_with_text(child_name, child_body));
    }

    fn tag_with_text_opt(&mut self, child_name: &'static str, child_body: &Option<String>) {
        if let Some(ref c) = *child_body {
            self.tag_with_text(child_name, &c);
        }
    }
}

fn elem_with_text(tag_name: &'static str, chars: &str) -> Element {
    let mut elem = Element::new(tag_name.to_string(), None, vec![]);
    elem.text(chars.to_string());
    elem
}

trait ViaXml {
    fn to_xml(&self) -> Element;
    fn from_xml(element: Element) -> Result<Self, &'static str>;
}

/// RSS
///
/// "At the top level, a RSS document is a \<rss\> element, with a mandatory attribute called
/// version, that specifies the version of RSS that the document conforms to. If it conforms to
/// this specification, the version attribute must be 2.0."
///
/// [RSS 2.0 Specification § RSS]
/// (http://cyber.law.harvard.edu/rss/rss.html#whatIsRss)
#[derive(Default)]
pub struct Rss(pub Channel);

impl ViaXml for Rss {
    fn to_xml(&self) -> Element {
        let mut rss = Element::new("rss".to_string(), None, vec![("version".to_string(), None, "2.0".to_string())]);

        let &Rss(ref channel) = self;
        rss.tag(channel.to_xml());

        rss
    }

    fn from_xml(rss_elem: Element) -> Result<Self, &'static str> {
        if rss_elem.name.to_ascii_lowercase() != "rss" {
            panic!("Expected <rss>, found <{}>", rss_elem.name);
        }

        let channel_elem = match rss_elem.get_child("channel", None) {
            Some(elem) => elem,
            None => return Err("No <channel> element found in <rss>"),
        };

        let channel = try!(ViaXml::from_xml(channel_elem.clone()));

        Ok(Rss(channel))
    }
}

impl Rss {
    pub fn to_string(&self) -> String {
        let mut ret = format!("{}", Xml::PINode("xml version='1.0' encoding='UTF-8'".to_string()));
        ret.push_str(&format!("{}", self.to_xml()));
        ret
    }

    pub fn from_read(reader: &mut io::Read) -> Result<Self, &'static str> {
        let mut rss_string = String::new();

        if let Err(..) = reader.read_to_string(&mut rss_string) {
            return Err("Error reading string from reader");
        }

        let mut parser = Parser::new();
        parser.feed_str(&rss_string);

        let mut builder = ElementBuilder::new();

        for event in parser {
            if let Some(Ok(element)) = builder.handle_event(event) {
                return ViaXml::from_xml(element);
            }
        }

        Err("RSS read error")
    }
}

/// Channel
///
/// "Subordinate to the \<rss\> element is a single \<channel\> element, which contains information
/// about the channel (metadata) and its contents."
///
/// [RSS 2.0 Specification § Channel]
/// (http://cyber.law.harvard.edu/rss/rss.html#requiredChannelElements)
///
/// ## Examples
///
/// ```
/// use rss::Channel;
/// use std::default::Default;
///
/// let channel = Channel {
///     title: "My Blog".to_string(),
///     link: "http://myblog.com".to_string(),
///     description: "Where I write stuff".to_string(),
///     items: vec![],
///     ..Default::default()
/// };
/// ```
#[derive(Default)]
pub struct Channel {
    pub title: String,
    pub link: String,
    pub description: String,
    pub items: Vec<Item>,
    pub language: Option<String>,
    pub copyright: Option<String>,
    pub managing_editor: Option<String>,
    pub web_master: Option<String>,
    pub pub_date: Option<String>,
    pub last_build_date: Option<String>,
    pub categories: Vec<Category>,
    pub generator: Option<String>,
    pub docs: Option<String>,
    // pub cloud:
    // pub ttl:
    pub image: Option<String>,
    pub rating: Option<String>,
    // pub text_input:
    pub skip_hours: Option<String>,
    pub skip_days: Option<String>,
}

impl ViaXml for Channel {
    fn to_xml(&self) -> Element {
        let mut channel = Element::new("channel".to_string(), None, vec![]);

        channel.tag_with_text("title", &self.title);
        channel.tag_with_text("link", &self.link);
        channel.tag_with_text("description", &self.description);

        for item in &self.items {
            channel.tag(item.to_xml());
        }

        channel.tag_with_text_opt("language", &self.language);
        channel.tag_with_text_opt("copyright", &self.copyright);
        channel.tag_with_text_opt("managingEditor", &self.managing_editor);
        channel.tag_with_text_opt("webMaster", &self.web_master);
        channel.tag_with_text_opt("pubDate", &self.pub_date);
        channel.tag_with_text_opt("lastBuildDate", &self.last_build_date);
        channel.tag_with_text_opt("generator", &self.generator);
        channel.tag_with_text_opt("docs", &self.docs);
        channel.tag_with_text_opt("image", &self.image);
        channel.tag_with_text_opt("rating", &self.rating);
        channel.tag_with_text_opt("skipHours", &self.skip_hours);
        channel.tag_with_text_opt("skipDays", &self.skip_days);

        for category in &self.categories {
            channel.tag(category.to_xml());
        }

        channel
    }

    fn from_xml(element: Element) -> Result<Self, &'static str> {
        let title = match element.get_child("title", None) {
            Some(element) => element.content_str(),
            None => return Err("<channel> is missing required <title> element"),
        };

        let link = match element.get_child("link", None) {
            Some(element) => element.content_str(),
            None => return Err("<channel> is missing required <link> element"),
        };

        let description = match element.get_child("description", None) {
            Some(element) => element.content_str(),
            None => return Err("<channel> is missing required <description> element"),
        };

        let items = element.get_children("item", None)
            .map(|e| ViaXml::from_xml(e.clone()).unwrap())
            .collect();

        let categories = element.get_children("category", None)
            .map(|e| ViaXml::from_xml(e.clone()).unwrap())
            .collect();

        Ok(Channel {
            title: title,
            link: link,
            description: description,
            items: items,
            categories: categories,
            ..Default::default()  // TODO
        })
    }
}

/// Item
///
/// [RSS 2.0 Specification § Item]
/// http://cyber.law.harvard.edu/rss/rss.html#hrelementsOfLtitemgt
#[derive(Default)]
pub struct Item {
    pub title: Option<String>,
    pub link: Option<String>,
    pub description: Option<String>,
    pub author: Option<String>,
    pub categories: Vec<Category>,
    pub comments: Option<String>,
    // pub enclosure
    // pub guid
    // pub pubDate
    // pub source
}

impl ViaXml for Item {
    fn to_xml(&self) -> Element {
        let mut item = Element::new("item".to_string(), None, vec![]);

        item.tag_with_text_opt("title", &self.title);
        item.tag_with_text_opt("link", &self.link);
        item.tag_with_text_opt("description", &self.description);
        item.tag_with_text_opt("author", &self.author);
        item.tag_with_text_opt("comments", &self.comments);

        for category in &self.categories {
            item.tag(category.to_xml());
        }

        item
    }

    fn from_xml(element: Element) -> Result<Self, &'static str> {
        let title = element.get_child("title", None).map(Element::content_str);
        let link = element.get_child("link", None).map(Element::content_str);
        let description = element.get_child("description", None).map(Element::content_str);
        let author = element.get_child("author", None).map(Element::content_str);
        let comments = element.get_child("comments", None).map(Element::content_str);

        let categories = element.get_children("category", None)
            .map(|e| ViaXml::from_xml(e.clone()).unwrap())
            .collect();

        Ok(Item {
            title: title,
            link: link,
            description: description,
            categories: categories,
            author: author,
            comments: comments,
        })
    }
}

/// Category
///
/// [RSS 2.0 Specification § Category]
/// (http://cyber.law.harvard.edu/rss/rss.html#ltcategorygtSubelementOfLtitemgt)
#[derive(Default)]
pub struct Category {
    pub domain: Option<String>,
    pub value: String,
}

impl ViaXml for Category {
    fn to_xml(&self) -> Element {
        let mut category = match self.domain {
            Some(ref d) => Element::new("category".to_string(), None, vec![("domain".to_string(), None, d.clone())]),
            None => Element::new("category".to_string(), None, vec![]),
        };
        category.text(self.value.clone());
        category
    }

    fn from_xml(elem: Element) -> Result<Self, &'static str> {
        let domain = elem.get_attribute("domain", None).map(|s| s.to_string());
        let value = elem.content_str();

        Ok(Category {
            domain: domain,
            value: value,
        })
    }
}

#[cfg(test)]
mod test {
    use std::default::Default;
    use std::fs::File;
    use super::{Rss, Item, Channel};

    #[test]
    fn test_basic_to_string() {
        let item = Item {
            title: Some("My first post!".to_string()),
            link: Some("http://myblog.com/post1".to_string()),
            description: Some("This is my first post".to_string()),
            ..Default::default()
        };

        let channel = Channel {
            title: "My Blog".to_string(),
            link: "http://myblog.com".to_string(),
            description: "Where I write stuff".to_string(),
            items: vec![item],
            ..Default::default()
        };

        let rss = Rss(channel);
        assert_eq!(rss.to_string(), "<?xml version=\'1.0\' encoding=\'UTF-8\'?><rss version=\'2.0\'><channel><title>My Blog</title><link>http://myblog.com</link><description>Where I write stuff</description><item><title>My first post!</title><link>http://myblog.com/post1</link><description>This is my first post</description></item></channel></rss>");
    }

    #[test]
    fn test_from_file() {
        let mut file = File::open("test-data/pinboard.xml").unwrap();
        let rss = Rss::from_read(&mut file).unwrap();
        assert!(rss.to_string().len() > 0);
    }

    #[test]
    #[should_panic]
    fn test_from_read_no_channels() {
        let mut rss_bytes = "<rss></rss>".as_bytes();
        let Rss(_) = Rss::from_read(&mut rss_bytes).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_from_read_one_channel_no_properties() {
        let mut rss_bytes = "<rss><channel></channel></rss>".as_bytes();
        let Rss(_) = Rss::from_read(&mut rss_bytes).unwrap();
    }

    #[test]
    fn test_from_read_one_channel() {
        let mut rss_bytes = "<rss><channel><title>Hello world!</title><description></description><link></link></channel></rss>".as_bytes();
        let Rss(channel) = Rss::from_read(&mut rss_bytes).unwrap();
        assert_eq!("Hello world!", channel.title);
    }
}
<|endoftext|>


<commit_before>pub trait Key: Copy + PartialEq + Ord + Default {
    type Bytes: AsRef<[u8]>;
    const SIZE: usize;
    fn into_bytes(self) -> Self::Bytes;
    fn from_bytes(bytes: Self::Bytes) -> Self;
}

macro_rules! impl_key {
    ($type:ident, $size:expr) => {
        impl Key for $type {
            type Bytes = [u8; $size];
            const SIZE: usize = $size;
            fn into_bytes(self) -> Self::Bytes {
                unsafe {
                    ::std::mem::transmute(self.to_be())
                }
            }

            fn from_bytes(bytes: Self::Bytes) -> Self {
                let integer = unsafe {
                    ::std::mem::transmute(bytes)
                };
                $type::from_be(integer)
            }
        }
    }
}

impl_key!(u8, 1);
impl_key!(u16, 2);
impl_key!(u32, 4);
impl_key!(u64, 8);
#[cfg(feature = "i128")]
impl_key!(u128, 16);
#[cfg(target_pointer_width = "32")]
impl_key!(usize, 4);
#[cfg(target_pointer_width = "64")]
impl_key!(usize, 8);
<commit_msg>Require that keys impl Debug<commit_after>use std::fmt::Debug;

pub trait Key: Copy + PartialEq + Ord + Default + Debug {
    type Bytes: AsRef<[u8]>;
    const SIZE: usize;
    fn into_bytes(self) -> Self::Bytes;
    fn from_bytes(bytes: Self::Bytes) -> Self;
}

macro_rules! impl_key {
    ($type:ident, $size:expr) => {
        impl Key for $type {
            type Bytes = [u8; $size];
            const SIZE: usize = $size;
            fn into_bytes(self) -> Self::Bytes {
                unsafe {
                    ::std::mem::transmute(self.to_be())
                }
            }

            fn from_bytes(bytes: Self::Bytes) -> Self {
                let integer = unsafe {
                    ::std::mem::transmute(bytes)
                };
                $type::from_be(integer)
            }
        }
    }
}

impl_key!(u8, 1);
impl_key!(u16, 2);
impl_key!(u32, 4);
impl_key!(u64, 8);
#[cfg(feature = "i128")]
impl_key!(u128, 16);
#[cfg(target_pointer_width = "32")]
impl_key!(usize, 4);
#[cfg(target_pointer_width = "64")]
impl_key!(usize, 8);
<|endoftext|>


<commit_before>#![feature(phase)]

extern crate serialize;
extern crate regex;

use std::sync::{Arc, RWLock};
use std::ascii::AsciiExt;
use std::collections::HashMap;
use std::task::TaskBuilder;

pub use errors::InitializationError;
pub use config::ClientConfiguration;
pub use interface::{CommandEvent, IrcMessageEvent, CtcpEvent, IrcInterface};
use irc::{IrcConnection, IrcMessage};

pub mod errors;
pub mod config;
pub mod interface;
mod irc;

pub fn get_version() -> String {
    return format!("{}.{}.{}{}",
        env!("CARGO_PKG_VERSION_MAJOR"),
        env!("CARGO_PKG_VERSION_MINOR"),
        env!("CARGO_PKG_VERSION_PATCH"),
        option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""));
}

// TODO: Store channels joined
pub struct Client {
    data_in: Receiver<Option<IrcMessage>>,
    pub interface: IrcInterface,
    commands: Arc<RWLock<HashMap<String, Vec<|&CommandEvent|:Send + Sync>>>>,
    ctcp_listeners: Arc<RWLock<HashMap<String, Vec<|&CtcpEvent|:Send + Sync>>>>,
    raw_listeners: Arc<RWLock<HashMap<String, Vec<|&IrcMessageEvent|:Send + Sync>>>>,
    catch_all: Arc<RWLock<Vec<|&IrcMessageEvent|:Send + Sync>>>,
    config: Arc<ClientConfiguration>,
    irc_connection_channel: Option<(Sender<Option<IrcMessage>>, Receiver<Option<String>>)>,
}

impl Client {
    pub fn new(config: ClientConfiguration) -> Result<Client, InitializationError> {
        let rc_config = Arc::new(config);
        let (data_out, connection_data_in) = channel();
        let (connection_data_out, data_in) = channel();
        let mut client = Client {
            interface: try!(IrcInterface::new(data_out, rc_config.clone())),
            data_in: data_in,
            commands: Arc::new(RWLock::new(HashMap::new())),
            raw_listeners: Arc::new(RWLock::new(HashMap::new())),
            ctcp_listeners: Arc::new(RWLock::new(HashMap::new())),
            catch_all: Arc::new(RWLock::new(Vec::new())),
            config: rc_config,
            irc_connection_channel: Some((connection_data_out, connection_data_in))
        };

        // Add initial channel join listener
        client.add_listener("004", |event: &IrcMessageEvent| {
            let nickserv = &event.client.config.nickserv;
            if nickserv.enabled {
                if nickserv.account.len() != 0 {
                    event.client.send_message(nickserv.name.as_slice(), format!("{} {} {}", nickserv.command, nickserv.account, nickserv.password).as_slice());
                } else {
                    event.client.send_message(nickserv.name.as_slice(), format!("{} {}", nickserv.command, nickserv.password).as_slice());
                }
            }

            for channel in event.client.config.channels.iter() {
                event.client.send_command("JOIN".into_string(), &[channel.as_slice()]);
            }
        });

        return Ok(client);
    }

    pub fn connect(mut self) -> Result<(), InitializationError> {
        // Get connection data_out/data_in, and assure that we haven't already done this (ensure we aren't already connected)
        let (connection_data_out, connection_data_in) = match self.irc_connection_channel {
            Some(v) => v,
            None => return Err(InitializationError::new("Already connected")),
        };
        self.irc_connection_channel = None;

        // Send NICK and USER, the initial IRC commands. Because an IrcConnection hasn't been created to receive these yet,
        //  they will just go on hold and get sent as soon as the IrcConnection connects.
        self.interface.send_command("NICK".into_string(), &[&*self.config.nick]);
        self.interface.send_command("USER".into_string(), &[&*self.config.user, "0", "*", &*format!(":{}", self.config.real_name)]);

        try!(IrcConnection::create(self.config.address.as_slice(), connection_data_out, connection_data_in));
        self.spawn_dispatch_thread();
        return Ok(());
    }

    pub fn add_listener(&mut self, irc_command: &str, f: |&IrcMessageEvent|:Send + Sync) {
        let command_string = irc_command.into_string().to_ascii_lower();

        let mut listener_map = self.raw_listeners.write();
        // I can't use a match here because then I would be borrowing listener_map mutably twice:
        // Once for the match statement, and a second time inside the None branch
        if listener_map.contains_key(&command_string) {
            listener_map.get_mut(&command_string).expect("Honestly, this won't happen.").push(f);
        } else {
            listener_map.insert(command_string, vec!(f));
        }
    }

    pub fn add_ctcp_listener(&mut self, ctcp_command: &str, f: |&CtcpEvent|:Send + Sync) {
        let command_string = ctcp_command.into_string().to_ascii_lower();

        let mut listener_map = self.ctcp_listeners.write();
        // I can't use a match here because then I would be borrowing listener_map mutably twice:
        // Once for the match statement, and a second time inside the None branch
        if listener_map.contains_key(&command_string) {
            listener_map.get_mut(&command_string).expect("Honestly, this won't happen.").push(f);
        } else {
            listener_map.insert(command_string, vec!(f));
        }
    }

    pub fn add_catch_all_listener(&mut self, f: |&IrcMessageEvent|:Send + Sync) {
        let mut listeners = self.catch_all.write();
        listeners.push(f);
    }

    pub fn add_command(&mut self, command: &str, f: |&CommandEvent|:Send + Sync) {
        let command_lower = command.into_string().to_ascii_lower();

        let mut command_map = self.commands.write();
        // I can't use a match here because then I would be borrowing the command_map mutably twice:
        // Once for the match statement, and a second time inside the None branch
        if command_map.contains_key(&command_lower) {
            command_map.get_mut(&command_lower).expect("Honestly, this won't happen.").push(f);
        } else {
            command_map.insert(command_lower, vec!(f));
        }
    }

    fn spawn_dispatch_thread(self) {
        TaskBuilder::new().named("client_dispatch_task").spawn(move || {
            loop {
                let message = match self.data_in.recv() {
                    Some(v) => v,
                    None => break,
                };
                self.process_message(&message);
            }
        });
    }

    // Noting: This has to be a separate method from spawn_dispatch_thread, so that we can name an 'a lifetime.
    // This allows us to give the new &str slices a specific lifetime, which I don't know a way to do without making a new function.
    fn process_message<'a>(&self, message: &'a IrcMessage) {
        // let shared_mask: Option<&str> = message.mask.as_ref().map(|s| &**s);
        let shared_mask = &interface::IrcMask::from_internal(&message.mask);
        let shared_args = message.args.iter().map(|s| &**s).collect::<Vec<&'a str>>();
        let shared_ctcp = message.ctcp.as_ref().map(|t| (t.0.as_slice(), t.1.as_slice()));

        // PING
        if message.command.as_slice().eq_ignore_ascii_case("PING") {
            self.interface.send_command("PONG".into_string(), shared_args.as_slice());
        }

        let message_event = &mut IrcMessageEvent::new(&self.interface, message.command.as_slice(), shared_args.as_slice(), shared_mask, shared_ctcp);

        // Catch all listeners
        {
            let mut catch_all = self.catch_all.write();
            for listener in catch_all.iter_mut() {
                (*listener)(message_event);
            }
        }

        // Raw listeners
        { // New scope so that listener_map will go out of scope after we use it
            let mut listener_map = self.raw_listeners.write();

            let listeners = listener_map.get_mut(&message.command.to_ascii_lower());
            if listeners.is_some() {
                for listener in listeners.unwrap().iter_mut() {
                    (*listener)(message_event);
                }
            }
        }

        if message.command.as_slice().eq_ignore_ascii_case("PRIVMSG") {

            // CTCP
            match message.ctcp {
                Some(ref t) => {
                    let ctcp_event = CtcpEvent::new(&self.interface, message.args[0].as_slice(), t.0.as_slice(), t.1.as_slice(), shared_mask);
                    { // New scope so that ctcp_map will go out of scope after we use it
                        let mut ctcp_map = self.ctcp_listeners.write();
                        let ctcp_listeners = ctcp_map.get_mut(&ctcp_event.command.to_ascii_lower());
                        if ctcp_listeners.is_some() {
                            for ctcp_listener in ctcp_listeners.unwrap().iter_mut() {
                                (*ctcp_listener)(&ctcp_event);
                            }
                        }
                    }
                },
                None => (),
            }

            // Commands
            let channel = shared_args[0];
            let prefix = format!(":{}", self.config.command_prefix.as_slice());
            if shared_args[1].starts_with(prefix.as_slice()) {
                let command = shared_args[1].slice_from(prefix.len()).into_string().to_ascii_lower();
                { // New scope so that command_map will go out of scope after we use it
                    let mut command_map = self.commands.write();
                    let commands = command_map.get_mut(&command);
                    if commands.is_some() {
                        let args = shared_args.slice_from(2);
                        let command_event = &mut CommandEvent::new(&self.interface, channel, args, shared_mask);

                        for command in commands.unwrap().iter_mut() {
                            (*command)(command_event);
                        }
                    }
                }
            }
        }
    }
}
<commit_msg>Switch to using #![feature(unboxed_closures)] so that we can have immutable closures<commit_after>#![feature(phase)]
#![feature(unboxed_closures)]

extern crate serialize;
extern crate regex;

use std::sync::{Arc, RWLock};
use std::ascii::AsciiExt;
use std::collections::HashMap;
use std::task::TaskBuilder;

pub use errors::InitializationError;
pub use config::ClientConfiguration;
pub use interface::{CommandEvent, IrcMessageEvent, CtcpEvent, IrcInterface};
use irc::{IrcConnection, IrcMessage};

pub mod errors;
pub mod config;
pub mod interface;
mod irc;

pub fn get_version() -> String {
    return format!("{}.{}.{}{}",
        env!("CARGO_PKG_VERSION_MAJOR"),
        env!("CARGO_PKG_VERSION_MINOR"),
        env!("CARGO_PKG_VERSION_PATCH"),
        option_env!("CARGO_PKG_VERSION_PRE").unwrap_or(""));
}

// TODO: Store channels joined
pub struct Client {
    data_in: Receiver<Option<IrcMessage>>,
    pub interface: IrcInterface,
    commands: Arc<RWLock<HashMap<String, Vec<Box<Fn(&CommandEvent) + Send + Sync>>>>>,
    ctcp_listeners: Arc<RWLock<HashMap<String, Vec<Box<Fn(&CtcpEvent) + Send + Sync>>>>>,
    raw_listeners: Arc<RWLock<HashMap<String, Vec<Box<Fn(&IrcMessageEvent) + Send + Sync>>>>>,
    catch_all: Arc<RWLock<Vec<Box<Fn(&IrcMessageEvent) + Send + Sync>>>>,
    config: Arc<ClientConfiguration>,
    irc_connection_channel: Option<(Sender<Option<IrcMessage>>, Receiver<Option<String>>)>,
}

impl Client {
    pub fn new(config: ClientConfiguration) -> Result<Client, InitializationError> {
        let rc_config = Arc::new(config);
        let (data_out, connection_data_in) = channel();
        let (connection_data_out, data_in) = channel();
        let mut client = Client {
            interface: try!(IrcInterface::new(data_out, rc_config.clone())),
            data_in: data_in,
            commands: Arc::new(RWLock::new(HashMap::new())),
            raw_listeners: Arc::new(RWLock::new(HashMap::new())),
            ctcp_listeners: Arc::new(RWLock::new(HashMap::new())),
            catch_all: Arc::new(RWLock::new(Vec::new())),
            config: rc_config,
            irc_connection_channel: Some((connection_data_out, connection_data_in))
        };

        // Add initial channel join listener
        client.add_listener("004", |event: &IrcMessageEvent| {
            let nickserv = &event.client.config.nickserv;
            if nickserv.enabled {
                if nickserv.account.len() != 0 {
                    event.client.send_message(nickserv.name.as_slice(), format!("{} {} {}", nickserv.command, nickserv.account, nickserv.password).as_slice());
                } else {
                    event.client.send_message(nickserv.name.as_slice(), format!("{} {}", nickserv.command, nickserv.password).as_slice());
                }
            }

            for channel in event.client.config.channels.iter() {
                event.client.send_command("JOIN".into_string(), &[channel.as_slice()]);
            }
        });

        return Ok(client);
    }

    pub fn connect(mut self) -> Result<(), InitializationError> {
        // Get connection data_out/data_in, and assure that we haven't already done this (ensure we aren't already connected)
        let (connection_data_out, connection_data_in) = match self.irc_connection_channel {
            Some(v) => v,
            None => return Err(InitializationError::new("Already connected")),
        };
        self.irc_connection_channel = None;

        // Send NICK and USER, the initial IRC commands. Because an IrcConnection hasn't been created to receive these yet,
        //  they will just go on hold and get sent as soon as the IrcConnection connects.
        self.interface.send_command("NICK".into_string(), &[&*self.config.nick]);
        self.interface.send_command("USER".into_string(), &[&*self.config.user, "0", "*", &*format!(":{}", self.config.real_name)]);

        try!(IrcConnection::create(self.config.address.as_slice(), connection_data_out, connection_data_in));
        self.spawn_dispatch_thread();
        return Ok(());
    }

    pub fn add_listener<T: Fn(&IrcMessageEvent) + Send + Sync>(&mut self, irc_command: &str, f: T) {
        let boxed = box f as Box<Fn(&IrcMessageEvent) + Send + Sync>;
        let command_string = irc_command.into_string().to_ascii_lower();

        let mut listener_map = self.raw_listeners.write();
        // I can't use a match here because then I would be borrowing listener_map mutably twice:
        // Once for the match statement, and a second time inside the None branch
        if listener_map.contains_key(&command_string) {
            listener_map.get_mut(&command_string).expect("Honestly, this won't happen.").push(boxed);
        } else {
            listener_map.insert(command_string, vec!(boxed));
        }
    }

    pub fn add_ctcp_listener<T: Fn(&CtcpEvent) + Send + Sync>(&mut self, ctcp_command: &str, f: T) {
        let boxed = box f as Box<Fn(&CtcpEvent) + Send + Sync>;
        let command_string = ctcp_command.into_string().to_ascii_lower();

        let mut listener_map = self.ctcp_listeners.write();
        // I can't use a match here because then I would be borrowing listener_map mutably twice:
        // Once for the match statement, and a second time inside the None branch
        if listener_map.contains_key(&command_string) {
            listener_map.get_mut(&command_string).expect("Honestly, this won't happen.").push(boxed);
        } else {
            listener_map.insert(command_string, vec!(boxed));
        }
    }

    pub fn add_catch_all_listener<T: Fn(&IrcMessageEvent) + Send + Sync>(&mut self, f: T) {
        self.catch_all.write().push(box f as Box<Fn(&IrcMessageEvent) + Send + Sync>);
    }

    pub fn add_command<T: Fn(&CommandEvent) + Send + Sync>(&mut self, command: &str, f: T) {
        let boxed = box f as Box<Fn(&CommandEvent) + Send + Sync>;
        let command_lower = command.into_string().to_ascii_lower();

        let mut command_map = self.commands.write();
        // I can't use a match here because then I would be borrowing the command_map mutably twice:
        // Once for the match statement, and a second time inside the None branch
        if command_map.contains_key(&command_lower) {
            command_map.get_mut(&command_lower).expect("Honestly, this won't happen.").push(boxed);
        } else {
            command_map.insert(command_lower, vec!(boxed));
        }
    }

    fn spawn_dispatch_thread(self) {
        TaskBuilder::new().named("client_dispatch_task").spawn(move || {
            loop {
                let message = match self.data_in.recv() {
                    Some(v) => v,
                    None => break,
                };
                self.process_message(&message);
            }
        });
    }

    // Noting: This has to be a separate method from spawn_dispatch_thread, so that we can name an 'a lifetime.
    // This allows us to give the new &str slices a specific lifetime, which I don't know a way to do without making a new function.
    fn process_message<'a>(&self, message: &'a IrcMessage) {
        // let shared_mask: Option<&str> = message.mask.as_ref().map(|s| &**s);
        let shared_mask = &interface::IrcMask::from_internal(&message.mask);
        let shared_args = message.args.iter().map(|s| &**s).collect::<Vec<&'a str>>();
        let shared_ctcp = message.ctcp.as_ref().map(|t| (t.0.as_slice(), t.1.as_slice()));

        // PING
        if message.command.as_slice().eq_ignore_ascii_case("PING") {
            self.interface.send_command("PONG".into_string(), shared_args.as_slice());
        }

        let message_event = &mut IrcMessageEvent::new(&self.interface, message.command.as_slice(), shared_args.as_slice(), shared_mask, shared_ctcp);

        // Catch all listeners
        {
            let catch_all = self.catch_all.read();
            for listener in catch_all.iter() {
                listener.call((message_event,));
            }
        }

        // Raw listeners
        { // New scope so that listener_map will go out of scope after we use it
            let listener_map = self.raw_listeners.read();

            let listeners = listener_map.get(&message.command.to_ascii_lower());
            match listeners {
                Some(list) => {
                    for listener in list.iter() {
                        listener.call((message_event,));
                    }
                },
                None => (),
            }
        }

        if message.command.as_slice().eq_ignore_ascii_case("PRIVMSG") {

            // CTCP
            match message.ctcp {
                Some(ref t) => {
                    let ctcp_event = CtcpEvent::new(&self.interface, message.args[0].as_slice(), t.0.as_slice(), t.1.as_slice(), shared_mask);
                    { // New scope so that ctcp_map will go out of scope after we use it
                        let ctcp_map = self.ctcp_listeners.read();
                        let ctcp_listeners = ctcp_map.get(&ctcp_event.command.to_ascii_lower());
                        match ctcp_listeners {
                            Some(list) => {
                                for ctcp_listener in list.iter() {
                                    ctcp_listener.call((&ctcp_event,));
                                }
                            },
                            None => (),
                        }
                    }
                },
                None => (),
            }

            // Commands
            let channel = shared_args[0];
            let prefix = format!(":{}", self.config.command_prefix.as_slice());
            if shared_args[1].starts_with(prefix.as_slice()) {
                let command = shared_args[1].slice_from(prefix.len()).into_string().to_ascii_lower();
                { // New scope so that command_map will go out of scope after we use it
                    let command_map = self.commands.read();
                    let commands = command_map.get(&command);
                    match commands {
                        Some(list) => {
                            let args = shared_args.slice_from(2);
                            let command_event = &mut CommandEvent::new(&self.interface, channel, args, shared_mask);
                            for command in list.iter() {
                                command.call((command_event,));
                            }
                        },
                        None => (),
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use bus::{Addressable, OAM_START, OAM_END, VIDEO_RAM_START, VIDEO_RAM_END};
use enum_primitive::FromPrimitive;

pub const SCREEN_WIDTH: usize = 160;
pub const SCREEN_HEIGHT: usize = 144;

const ADDR_LCDC: u16 = 0xFF40;
const ADDR_STAT: u16 = 0xFF41;
const ADDR_SCY: u16  = 0xFF42;
const ADDR_SCX: u16  = 0xFF43;
const ADDR_LY: u16   = 0xFF44;
const ADDR_LYC: u16  = 0xFF45;
pub const ADDR_DMA: u16  = 0xFF46;
const ADDR_BGP: u16  = 0xFF47;
const ADDR_OBP0: u16 = 0xFF48;
const ADDR_OBP1: u16 = 0xFF49;
const ADDR_WY: u16   = 0xFF4A;
const ADDR_WX: u16   = 0xFF4B;

bitflags! {
    flags Lcdc: u8 {
        const LCDC_ENABLED            = 0b1000_0000,
        const LCDC_WIN_TILE_9C        = 0b0100_0000,
        const LCDC_WIN_DISPLAY        = 0b0010_0000,
        const LCDC_UNSIGNED_TILE_DATA = 0b0001_0000,
        const LCDC_BG_TILE_9C         = 0b0000_1000,
        const LCDC_16PX_SPRITE        = 0b0000_0100,
        const LCDC_SPRITE_DISPLAY     = 0b0000_0010,
        const LCDC_BG_DISPLAY         = 0b0000_0001
    }
}

// STAT is mostly flags, but the lower 2 bits are the current mode
// Mode is will stored separately so the Stat struct is only treated as bitflags.
bitflags! {
    flags Stat: u8 {
        const STAT_COINCIDENCE_INT   = 0b0100_0000,
        const STAT_OAM_INT           = 0b0010_0000,
        const STAT_VBLANK_INT        = 0b0001_0000,
        const STAT_HBLANK_INT        = 0b0000_1000,
        const STAT_COINCIDENCE_EQUAL = 0b0000_0100
    }
}

enum_from_primitive! {
#[derive(Copy, Clone, PartialEq)]
pub enum Mode {
    VBlank   = 0,
    HBlank   = 1,
    Oam      = 2,
    Transfer = 3
}
}

enum_from_primitive! {
#[derive(Copy, Clone, PartialEq)]
enum Shade {
    White       = 0,
    LightGray   = 1,
    DarkGray    = 2,
    Black       = 3
}
}

const CYCLES_PER_OAM_READ: usize = 80;
const CYCLES_PER_TRANSFER: usize = 172;
const CYCLES_PER_HBLANK: usize = 204;
const CYCLES_PER_LINE: usize = 456;

#[derive(Copy, Clone)]
struct Palette(u8);

impl Palette {
    fn rgb(&self, shade: Shade) -> u32 {
        match shade {
            Shade::White        => 0x9CBD0F,
            Shade::LightGray    => 0x8CAD0F,
            Shade::DarkGray     => 0x306230,
            Shade::Black        => 0x0F380F
        }
    }
}

#[derive(Default)]
pub struct StepResult {
    pub int_vblank: bool,
    pub int_stat: bool
}

pub struct Lcd {
    vram: [u8; (VIDEO_RAM_END - VIDEO_RAM_START) as usize + 1],
    oam: [u8; (OAM_END - OAM_START) as usize + 1],
    lcdc: Lcdc,
    stat: Stat,
    scy: u8,
    scx: u8,
    ly: u8,
    lyc: u8,
    bgp: Palette,
    obp0: Palette,
    obp1: Palette,
    wy: u8,
    wx: u8,
    dma: u8,
    mode: Mode,
    mode_cycles: usize,
    draw_pending: bool
}

impl Lcd {
    pub fn new() -> Self {
        Lcd {
            vram: [0; (VIDEO_RAM_END - VIDEO_RAM_START) as usize + 1],
            oam: [0; (OAM_END - OAM_START) as usize + 1],
            lcdc: Lcdc::from_bits(0x91).unwrap(),
            stat: Stat::empty(),
            scy: 0,
            scx: 0,
            ly: 0,
            lyc: 0,
            bgp: Palette(0xFC),
            obp0: Palette(0xFF),
            obp1: Palette(0xFF),
            wy: 0,
            wx: 0,
            dma: 0,
            mode: Mode::Oam,
            mode_cycles: 0,
            draw_pending: false
        }
    }

    pub fn step(&mut self, cycles: usize, mut screen_buffer: &mut [u32]) -> StepResult {
        let mut result = StepResult::default();

        if self.lcdc.contains(LCDC_ENABLED) {
            let previous_mode = self.mode;
            self.mode_cycles = self.mode_cycles + cycles;

            match self.mode {
                Mode::Oam => {
                    // Process starts with the LCD controller reading information from OAM.
                    // This lasts 77-83 cycles
                    if self.mode_cycles >= CYCLES_PER_OAM_READ {
                        // Move onto transfer stage
                        self.mode = Mode::Transfer;
                        self.mode_cycles = 0;
                    }
                },
                Mode::Transfer => {
                    // Data is being actively read by the LCD driver from OAM & VRAM.
                    // This lasts 169-175 cycles
                    if self.mode_cycles >= CYCLES_PER_TRANSFER {
                        // Once transfer is complete, enter HBLANK
                        self.mode = Mode::HBlank;
                        self.mode_cycles = 0;
                        self.draw_pending = true;
                    }
                },
                Mode::HBlank => {
                    // LCD has reached the end of a line.
                    // HBlank phase lasts 201-207 cycles
                    if self.mode_cycles >= CYCLES_PER_HBLANK {
                        // LCD is ready to begin the next line
                        self.ly = self.ly + 1;
                        self.mode_cycles = 0;

                        // If the LCD has line 144, then enter VBLANK. Else, enter OAM read.
                        if self.ly == 144 {
                            self.mode = Mode::VBlank;
                            self.mode_cycles = 0;
                            result.int_vblank = true;

                            if self.lcdc.contains(LCDC_SPRITE_DISPLAY) {
                                self.draw_sprites(screen_buffer);
                            }
                        } else {
                            self.mode = Mode::Oam;
                        };

                        self.check_coincidence(&mut result);
                    } else {
                        if self.ly < 144 && self.draw_pending {
                            if self.lcdc.contains(LCDC_BG_DISPLAY) {
                                self.draw_background_line(screen_buffer);
                            }

                            self.draw_pending = false;
                        }
                    }
                },
                Mode::VBlank => {
                    // LCD is writing to VBlank lines
                    if self.mode_cycles >= CYCLES_PER_LINE {
                        self.mode_cycles = 0;
                        self.ly = self.ly + 1;

                        if self.ly > 153 {
                            // Finished with VBlank. Enter OAM with the first line.
                            self.ly = 0;
                            self.mode = Mode::Oam;
                        }

                        self.check_coincidence(&mut result);
                    }
                }
            }

            // Check if a new mode has been entered during this step.
            // If so, an interrupt will be raised if the respective flag is set in the STAT register
            if self.mode != previous_mode {
                let flag = match self.mode {
                    Mode::VBlank   => Some(STAT_VBLANK_INT),
                    Mode::HBlank   => Some(STAT_HBLANK_INT),
                    Mode::Oam      => Some(STAT_OAM_INT),
                    Mode::Transfer => None
                };

                if flag.is_some() && self.stat.contains(flag.unwrap()) {
                    result.int_stat = true;
                }
            }
        }

        result
    }

    fn draw_background_line(&self, screen_buffer: &mut [u32]) {
        let map_y = self.scy.wrapping_add(self.ly);
        let map_tile_y = (map_y / 8) as u16;
        let tile_row = map_y & 7;

        let map_addr_base = if self.lcdc.contains(LCDC_BG_TILE_9C) {
            0x9C00
        } else {
            0x9800
        } as u16;

        for screen_x in 0..SCREEN_WIDTH {
            let map_x = self.scx.wrapping_add(screen_x as u8);
            let map_tile_x = (map_x / 8) as u16;
            let tile_column = map_x & 7;

            let tile_idx_addr = map_addr_base + (map_tile_y * 32) + map_tile_x;
            let tile_idx = self.vram[(tile_idx_addr - VIDEO_RAM_START) as usize];
            let tile_addr = self.bg_tile_address(tile_idx);
            let shade = self.tile_pixel_shade(tile_addr, tile_row, tile_column);

            screen_buffer[(self.ly as usize * SCREEN_WIDTH) + screen_x] = self.bgp.rgb(shade);
        }
    }

    fn draw_sprites(&self, screen_buffer: &mut [u32]) {
        // Sprites are 40 blocks in OAM. Each block is 32 bits
        for idx in 0..40 {
            let y = self.oam[idx * 4] as usize;
            let x = self.oam[(idx * 4) + 1] as usize;
            let pattern =  self.oam[(idx * 4) + 2];
            let flags = self.oam[(idx * 4) + 3];

            if x >= 8 && y >= 16 && x < 160 && y < 144 {
                let tile_addr = self.sprite_tile_address(pattern);
                let screen_x = x.wrapping_sub(8) as usize;
                let screen_y = y.wrapping_sub(16) as usize;
                let palette = if flags & 0b10000 == 0b10000 { self.obp1 } else { self.obp0 };

                for row in 0..8 as usize {
                    for column in 0..8 as usize {
                        let shade = self.tile_pixel_shade(tile_addr, row as u8, column as u8);

                        if shade != Shade::White {
                            screen_buffer[((screen_y + row) * SCREEN_WIDTH) + screen_x + column] = palette.rgb(shade);
                        }
                    }
                }
            }
        }
    }

    fn tile_pixel_shade(&self, address: u16, row: u8, column: u8) -> Shade {
        let upper_byte = self.vram[((address + (row * 2) as u16) + 1 - VIDEO_RAM_START) as usize];
        let lower_byte = self.vram[((address + (row * 2) as u16) - VIDEO_RAM_START) as usize];

        let shift = 7 - column;
        let mask = 1 << shift;
        let upper_bit = (upper_byte & mask) >> shift;
        let lower_bit = (lower_byte & mask) >> shift;

        Shade::from_u8((upper_bit << 1) | lower_bit).unwrap()
    }

    fn check_coincidence(&mut self, result: &mut StepResult) {
        if self.stat.contains(STAT_COINCIDENCE_INT) {
            if self.ly == self.lyc {
                self.stat.insert(STAT_COINCIDENCE_EQUAL);
                result.int_stat = true;
            } else {
                self.stat.remove(STAT_COINCIDENCE_EQUAL);
            }
        } else {
            self.stat.remove(STAT_COINCIDENCE_EQUAL);
        }
    }

    fn bg_tile_address(&self, tile_index: u8) -> u16 {
        let tile_addr_base = if self.lcdc.contains(LCDC_UNSIGNED_TILE_DATA) {
            0x8000
        } else {
            0x9000
        } as u16;

        if self.lcdc.contains(LCDC_UNSIGNED_TILE_DATA) {
            (tile_addr_base + ((tile_index as u16) * 16))
        } else {
            let signed_index = (tile_index as i8) as i16;
            ((tile_addr_base as i16) + ((signed_index) * 16)) as u16
        }
    }

    fn sprite_tile_address(&self, tile_index: u8) -> u16 {
        0x8000 + ((tile_index as u16) * 16)
    }
}

impl Addressable for Lcd {
    fn read(&self, addr: u16) -> u8 {
        match addr {
            VIDEO_RAM_START...VIDEO_RAM_END => self.vram[(addr - VIDEO_RAM_START) as usize],
            OAM_START...OAM_END => self.oam[(addr - OAM_START) as usize],
            ADDR_LCDC => self.lcdc.bits,
            ADDR_STAT => 0b1000_0000 | ((self.stat.bits & 0b1111_1100) | (self.mode as u8)),
            ADDR_SCY => self.scy,
            ADDR_SCX => self.scx,
            ADDR_LY => self.ly,
            ADDR_LYC => self.lyc,
            ADDR_DMA => self.dma,
            ADDR_BGP => self.bgp.0,
            ADDR_OBP0 => self.obp0.0,
            ADDR_OBP1 => self.obp1.0,
            ADDR_WY => self.wy,
            ADDR_WX => self.wx,
            _ => { println!("LCD IO read unimplemented ({:#X})", addr); 0 }
        }
    }

    fn write(&mut self, addr: u16, val: u8) {
        match addr {
            VIDEO_RAM_START...VIDEO_RAM_END => self.vram[(addr - VIDEO_RAM_START) as usize] = val,
            OAM_START...OAM_END => self.oam[(addr - OAM_START) as usize] = val,
            ADDR_LCDC => self.lcdc.bits = val,
            ADDR_STAT => {
                self.stat.bits = val & 0b1111_1100;
                self.mode = Mode::from_u8(val & 0b11).unwrap();
            },
            ADDR_SCY => self.scy = val,
            ADDR_SCX => self.scx = val,
            ADDR_LY => (), // read-only
            ADDR_LYC => self.lyc = val,
            ADDR_DMA => { self.dma = val }, // Actual transfer is done by the Bus object
            ADDR_BGP => self.bgp = Palette(val),
            ADDR_OBP0 => self.obp0 = Palette(val),
            ADDR_OBP1 => self.obp1 = Palette(val),
            ADDR_WY => self.wy = val,
            ADDR_WX => self.wx = val,
            _ => println!("LCD IO write unimplemented {:#X} -> {:#X}", val, addr)
        }
    }
}<commit_msg>Sprite fix Sprites toward the bottom of the screen weren't being displayed.<commit_after>use bus::{Addressable, OAM_START, OAM_END, VIDEO_RAM_START, VIDEO_RAM_END};
use enum_primitive::FromPrimitive;

pub const SCREEN_WIDTH: usize = 160;
pub const SCREEN_HEIGHT: usize = 144;

const ADDR_LCDC: u16 = 0xFF40;
const ADDR_STAT: u16 = 0xFF41;
const ADDR_SCY: u16  = 0xFF42;
const ADDR_SCX: u16  = 0xFF43;
const ADDR_LY: u16   = 0xFF44;
const ADDR_LYC: u16  = 0xFF45;
pub const ADDR_DMA: u16  = 0xFF46;
const ADDR_BGP: u16  = 0xFF47;
const ADDR_OBP0: u16 = 0xFF48;
const ADDR_OBP1: u16 = 0xFF49;
const ADDR_WY: u16   = 0xFF4A;
const ADDR_WX: u16   = 0xFF4B;

bitflags! {
    flags Lcdc: u8 {
        const LCDC_ENABLED            = 0b1000_0000,
        const LCDC_WIN_TILE_9C        = 0b0100_0000,
        const LCDC_WIN_DISPLAY        = 0b0010_0000,
        const LCDC_UNSIGNED_TILE_DATA = 0b0001_0000,
        const LCDC_BG_TILE_9C         = 0b0000_1000,
        const LCDC_16PX_SPRITE        = 0b0000_0100,
        const LCDC_SPRITE_DISPLAY     = 0b0000_0010,
        const LCDC_BG_DISPLAY         = 0b0000_0001
    }
}

// STAT is mostly flags, but the lower 2 bits are the current mode
// Mode is will stored separately so the Stat struct is only treated as bitflags.
bitflags! {
    flags Stat: u8 {
        const STAT_COINCIDENCE_INT   = 0b0100_0000,
        const STAT_OAM_INT           = 0b0010_0000,
        const STAT_VBLANK_INT        = 0b0001_0000,
        const STAT_HBLANK_INT        = 0b0000_1000,
        const STAT_COINCIDENCE_EQUAL = 0b0000_0100
    }
}

enum_from_primitive! {
#[derive(Copy, Clone, PartialEq)]
pub enum Mode {
    VBlank   = 0,
    HBlank   = 1,
    Oam      = 2,
    Transfer = 3
}
}

enum_from_primitive! {
#[derive(Copy, Clone, PartialEq)]
enum Shade {
    White       = 0,
    LightGray   = 1,
    DarkGray    = 2,
    Black       = 3
}
}

const CYCLES_PER_OAM_READ: usize = 80;
const CYCLES_PER_TRANSFER: usize = 172;
const CYCLES_PER_HBLANK: usize = 204;
const CYCLES_PER_LINE: usize = 456;

#[derive(Copy, Clone)]
struct Palette(u8);

impl Palette {
    fn rgb(&self, shade: Shade) -> u32 {
        match shade {
            Shade::White        => 0x9CBD0F,
            Shade::LightGray    => 0x8CAD0F,
            Shade::DarkGray     => 0x306230,
            Shade::Black        => 0x0F380F
        }
    }
}

#[derive(Default)]
pub struct StepResult {
    pub int_vblank: bool,
    pub int_stat: bool
}

pub struct Lcd {
    vram: [u8; (VIDEO_RAM_END - VIDEO_RAM_START) as usize + 1],
    oam: [u8; (OAM_END - OAM_START) as usize + 1],
    lcdc: Lcdc,
    stat: Stat,
    scy: u8,
    scx: u8,
    ly: u8,
    lyc: u8,
    bgp: Palette,
    obp0: Palette,
    obp1: Palette,
    wy: u8,
    wx: u8,
    dma: u8,
    mode: Mode,
    mode_cycles: usize,
    draw_pending: bool
}

impl Lcd {
    pub fn new() -> Self {
        Lcd {
            vram: [0; (VIDEO_RAM_END - VIDEO_RAM_START) as usize + 1],
            oam: [0; (OAM_END - OAM_START) as usize + 1],
            lcdc: Lcdc::from_bits(0x91).unwrap(),
            stat: Stat::empty(),
            scy: 0,
            scx: 0,
            ly: 0,
            lyc: 0,
            bgp: Palette(0xFC),
            obp0: Palette(0xFF),
            obp1: Palette(0xFF),
            wy: 0,
            wx: 0,
            dma: 0,
            mode: Mode::Oam,
            mode_cycles: 0,
            draw_pending: false
        }
    }

    pub fn step(&mut self, cycles: usize, mut screen_buffer: &mut [u32]) -> StepResult {
        let mut result = StepResult::default();

        if self.lcdc.contains(LCDC_ENABLED) {
            let previous_mode = self.mode;
            self.mode_cycles = self.mode_cycles + cycles;

            match self.mode {
                Mode::Oam => {
                    // Process starts with the LCD controller reading information from OAM.
                    // This lasts 77-83 cycles
                    if self.mode_cycles >= CYCLES_PER_OAM_READ {
                        // Move onto transfer stage
                        self.mode = Mode::Transfer;
                        self.mode_cycles = 0;
                    }
                },
                Mode::Transfer => {
                    // Data is being actively read by the LCD driver from OAM & VRAM.
                    // This lasts 169-175 cycles
                    if self.mode_cycles >= CYCLES_PER_TRANSFER {
                        // Once transfer is complete, enter HBLANK
                        self.mode = Mode::HBlank;
                        self.mode_cycles = 0;
                        self.draw_pending = true;
                    }
                },
                Mode::HBlank => {
                    // LCD has reached the end of a line.
                    // HBlank phase lasts 201-207 cycles
                    if self.mode_cycles >= CYCLES_PER_HBLANK {
                        // LCD is ready to begin the next line
                        self.ly = self.ly + 1;
                        self.mode_cycles = 0;

                        // If the LCD has line 144, then enter VBLANK. Else, enter OAM read.
                        if self.ly == 144 {
                            self.mode = Mode::VBlank;
                            self.mode_cycles = 0;
                            result.int_vblank = true;

                            if self.lcdc.contains(LCDC_SPRITE_DISPLAY) {
                                self.draw_sprites(screen_buffer);
                            }
                        } else {
                            self.mode = Mode::Oam;
                        };

                        self.check_coincidence(&mut result);
                    } else {
                        if self.ly < 144 && self.draw_pending {
                            if self.lcdc.contains(LCDC_BG_DISPLAY) {
                                self.draw_background_line(screen_buffer);
                            }

                            self.draw_pending = false;
                        }
                    }
                },
                Mode::VBlank => {
                    // LCD is writing to VBlank lines
                    if self.mode_cycles >= CYCLES_PER_LINE {
                        self.mode_cycles = 0;
                        self.ly = self.ly + 1;

                        if self.ly > 153 {
                            // Finished with VBlank. Enter OAM with the first line.
                            self.ly = 0;
                            self.mode = Mode::Oam;
                        }

                        self.check_coincidence(&mut result);
                    }
                }
            }

            // Check if a new mode has been entered during this step.
            // If so, an interrupt will be raised if the respective flag is set in the STAT register
            if self.mode != previous_mode {
                let flag = match self.mode {
                    Mode::VBlank   => Some(STAT_VBLANK_INT),
                    Mode::HBlank   => Some(STAT_HBLANK_INT),
                    Mode::Oam      => Some(STAT_OAM_INT),
                    Mode::Transfer => None
                };

                if flag.is_some() && self.stat.contains(flag.unwrap()) {
                    result.int_stat = true;
                }
            }
        }

        result
    }

    fn draw_background_line(&self, screen_buffer: &mut [u32]) {
        let map_y = self.scy.wrapping_add(self.ly);
        let map_tile_y = (map_y / 8) as u16;
        let tile_row = map_y & 7;

        let map_addr_base = if self.lcdc.contains(LCDC_BG_TILE_9C) {
            0x9C00
        } else {
            0x9800
        } as u16;

        for screen_x in 0..SCREEN_WIDTH {
            let map_x = self.scx.wrapping_add(screen_x as u8);
            let map_tile_x = (map_x / 8) as u16;
            let tile_column = map_x & 7;

            let tile_idx_addr = map_addr_base + (map_tile_y * 32) + map_tile_x;
            let tile_idx = self.vram[(tile_idx_addr - VIDEO_RAM_START) as usize];
            let tile_addr = self.bg_tile_address(tile_idx);
            let shade = self.tile_pixel_shade(tile_addr, tile_row, tile_column);

            screen_buffer[(self.ly as usize * SCREEN_WIDTH) + screen_x] = self.bgp.rgb(shade);
        }
    }

    fn draw_sprites(&self, screen_buffer: &mut [u32]) {
        // Sprites are 40 blocks in OAM. Each block is 32 bits
        for idx in 0..40 {
            let y = self.oam[idx * 4] as usize;
            let x = self.oam[(idx * 4) + 1] as usize;
            let pattern =  self.oam[(idx * 4) + 2];
            let flags = self.oam[(idx * 4) + 3];

            if x >= 8 && y >= 16 {
                let tile_addr = self.sprite_tile_address(pattern);
                let screen_x = x.wrapping_sub(8) as usize;
                let screen_y = y.wrapping_sub(16) as usize;
                let palette = if flags & 0b10000 == 0b10000 { self.obp1 } else { self.obp0 };

                for row in 0..8 as usize {
                    for column in 0..8 as usize {
                        let idx = ((screen_y + row) * SCREEN_WIDTH) + screen_x + column;
                        let shade = self.tile_pixel_shade(tile_addr, row as u8, column as u8);

                        if idx < screen_buffer.len() && shade != Shade::White {
                            screen_buffer[idx] = palette.rgb(shade);
                        }
                    }
                }
            }
        }
    }

    fn tile_pixel_shade(&self, address: u16, row: u8, column: u8) -> Shade {
        let upper_byte = self.vram[((address + (row * 2) as u16) + 1 - VIDEO_RAM_START) as usize];
        let lower_byte = self.vram[((address + (row * 2) as u16) - VIDEO_RAM_START) as usize];

        let shift = 7 - column;
        let mask = 1 << shift;
        let upper_bit = (upper_byte & mask) >> shift;
        let lower_bit = (lower_byte & mask) >> shift;

        Shade::from_u8((upper_bit << 1) | lower_bit).unwrap()
    }

    fn check_coincidence(&mut self, result: &mut StepResult) {
        if self.stat.contains(STAT_COINCIDENCE_INT) {
            if self.ly == self.lyc {
                self.stat.insert(STAT_COINCIDENCE_EQUAL);
                result.int_stat = true;
            } else {
                self.stat.remove(STAT_COINCIDENCE_EQUAL);
            }
        } else {
            self.stat.remove(STAT_COINCIDENCE_EQUAL);
        }
    }

    fn bg_tile_address(&self, tile_index: u8) -> u16 {
        let tile_addr_base = if self.lcdc.contains(LCDC_UNSIGNED_TILE_DATA) {
            0x8000
        } else {
            0x9000
        } as u16;

        if self.lcdc.contains(LCDC_UNSIGNED_TILE_DATA) {
            (tile_addr_base + ((tile_index as u16) * 16))
        } else {
            let signed_index = (tile_index as i8) as i16;
            ((tile_addr_base as i16) + ((signed_index) * 16)) as u16
        }
    }

    fn sprite_tile_address(&self, tile_index: u8) -> u16 {
        0x8000 + ((tile_index as u16) * 16)
    }
}

impl Addressable for Lcd {
    fn read(&self, addr: u16) -> u8 {
        match addr {
            VIDEO_RAM_START...VIDEO_RAM_END => self.vram[(addr - VIDEO_RAM_START) as usize],
            OAM_START...OAM_END => self.oam[(addr - OAM_START) as usize],
            ADDR_LCDC => self.lcdc.bits,
            ADDR_STAT => 0b1000_0000 | ((self.stat.bits & 0b1111_1100) | (self.mode as u8)),
            ADDR_SCY => self.scy,
            ADDR_SCX => self.scx,
            ADDR_LY => self.ly,
            ADDR_LYC => self.lyc,
            ADDR_DMA => self.dma,
            ADDR_BGP => self.bgp.0,
            ADDR_OBP0 => self.obp0.0,
            ADDR_OBP1 => self.obp1.0,
            ADDR_WY => self.wy,
            ADDR_WX => self.wx,
            _ => { println!("LCD IO read unimplemented ({:#X})", addr); 0 }
        }
    }

    fn write(&mut self, addr: u16, val: u8) {
        match addr {
            VIDEO_RAM_START...VIDEO_RAM_END => self.vram[(addr - VIDEO_RAM_START) as usize] = val,
            OAM_START...OAM_END => self.oam[(addr - OAM_START) as usize] = val,
            ADDR_LCDC => self.lcdc.bits = val,
            ADDR_STAT => {
                self.stat.bits = val & 0b1111_1100;
                self.mode = Mode::from_u8(val & 0b11).unwrap();
            },
            ADDR_SCY => self.scy = val,
            ADDR_SCX => self.scx = val,
            ADDR_LY => (), // read-only
            ADDR_LYC => self.lyc = val,
            ADDR_DMA => { self.dma = val }, // Actual transfer is done by the Bus object
            ADDR_BGP => self.bgp = Palette(val),
            ADDR_OBP0 => self.obp0 = Palette(val),
            ADDR_OBP1 => self.obp1 = Palette(val),
            ADDR_WY => self.wy = val,
            ADDR_WX => self.wx = val,
            _ => println!("LCD IO write unimplemented {:#X} -> {:#X}", val, addr)
        }
    }
}<|endoftext|>