<commit_before>pub mod requests;
pub use requests::*;

pub use telegram_bot_raw::{Integer, Float, Request};
pub use telegram_bot_raw::{Update, UpdateKind};
pub use telegram_bot_raw::{User, Group, Supergroup, Channel, Chat};
pub use telegram_bot_raw::{Message, MessageKind, Forward, ForwardFrom};
pub use telegram_bot_raw::{MessageEntity, MessageEntityKind};
pub use telegram_bot_raw::{Audio, Document, PhotoSize, Sticker, Video, Voice};
pub use telegram_bot_raw::{Contact, Location, Venue};

pub use telegram_bot_raw::{ParseMode, ChatId, UserId, MessageId};
pub use telegram_bot_raw::{ReplyMarkup, InlineKeyboardMarkup, ReplyKeyboardMarkup};
pub use telegram_bot_raw::{ReplyKeyboardRemove, ForceReply};
pub use telegram_bot_raw::{ChatAction};
<commit_msg>feat: export File type<commit_after>pub mod requests;
pub use requests::*;

pub use telegram_bot_raw::{Integer, Float, Request};
pub use telegram_bot_raw::{Update, UpdateKind};
pub use telegram_bot_raw::{User, Group, Supergroup, Channel, Chat};
pub use telegram_bot_raw::{Message, MessageKind, Forward, ForwardFrom};
pub use telegram_bot_raw::{MessageEntity, MessageEntityKind};
pub use telegram_bot_raw::{Audio, Document, PhotoSize, Sticker, Video, Voice};
pub use telegram_bot_raw::{Contact, Location, Venue, File};

pub use telegram_bot_raw::{ParseMode, ChatId, UserId, MessageId};
pub use telegram_bot_raw::{ReplyMarkup, InlineKeyboardMarkup, ReplyKeyboardMarkup};
pub use telegram_bot_raw::{ReplyKeyboardRemove, ForceReply};
pub use telegram_bot_raw::{ChatAction};
<|endoftext|>


<commit_before>#![cfg_attr(feature = "serde_derive", feature(proc_macro))]

#[cfg(feature = "serde_derive")]
#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

#[cfg(feature = "serde_derive")]
include!("main.rs.in");

#[cfg(not(feature = "serde_derive"))]
include!(concat!(env!("OUT_DIR"), "/main.rs"));
<commit_msg>Remove the proc_macro feature gate in serde-syntex-example<commit_after>#[cfg(feature = "serde_derive")]
#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

#[cfg(feature = "serde_derive")]
include!("main.rs.in");

#[cfg(not(feature = "serde_derive"))]
include!(concat!(env!("OUT_DIR"), "/main.rs"));
<|endoftext|>


<commit_before>use rustc_hash::{FxHashMap, FxHashSet};

use ra_syntax::{
    AstNode, SmolStr, SyntaxNodeRef, TextUnit, TextRange,
    algo::generate,
    ast::{self, ArgListOwner, LoopBodyOwner, NameOwner},
};
use ra_db::LocalSyntaxPtr;

use crate::{
    arena::{Arena, Id},
};

pub(crate) type ScopeId = Id<ScopeData>;

#[derive(Debug, PartialEq, Eq)]
pub struct FnScopes {
    pub self_param: Option<LocalSyntaxPtr>,
    scopes: Arena<ScopeData>,
    scope_for: FxHashMap<LocalSyntaxPtr, ScopeId>,
}

#[derive(Debug, PartialEq, Eq)]
pub struct ScopeEntry {
    name: SmolStr,
    ptr: LocalSyntaxPtr,
}

#[derive(Debug, PartialEq, Eq)]
pub struct ScopeData {
    parent: Option<ScopeId>,
    entries: Vec<ScopeEntry>,
}

impl FnScopes {
    pub fn new(fn_def: ast::FnDef) -> FnScopes {
        let mut scopes = FnScopes {
            self_param: fn_def
                .param_list()
                .and_then(|it| it.self_param())
                .map(|it| LocalSyntaxPtr::new(it.syntax())),
            scopes: Arena::default(),
            scope_for: FxHashMap::default(),
        };
        let root = scopes.root_scope();
        scopes.add_params_bindings(root, fn_def.param_list());
        if let Some(body) = fn_def.body() {
            compute_block_scopes(body, &mut scopes, root)
        }
        scopes
    }
    pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
        &self.scopes[scope].entries
    }
    pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a {
        generate(self.scope_for(node), move |&scope| {
            self.scopes[scope].parent
        })
    }
    pub fn scope_chain_for_offset<'a>(
        &'a self,
        offset: TextUnit,
    ) -> impl Iterator<Item = ScopeId> + 'a {
        let scope = self
            .scope_for
            .iter()
            // find containin scope
            .min_by_key(|(ptr, _scope)| {
                (
                    !(ptr.range().start() <= offset && offset <= ptr.range().end()),
                    ptr.range().len(),
                )
            })
            .map(|(ptr, scope)| self.adjust(*ptr, *scope, offset));

        generate(scope, move |&scope| self.scopes[scope].parent)
    }
    // XXX: during completion, cursor might be outside of any particular
    // expression. Try to figure out the correct scope...
    fn adjust(&self, ptr: LocalSyntaxPtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId {
        let r = ptr.range();
        let child_scopes = self
            .scope_for
            .iter()
            .map(|(ptr, scope)| (ptr.range(), scope))
            .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r);

        child_scopes
            .max_by(|(r1, _), (r2, _)| {
                if r2.is_subrange(&r1) {
                    std::cmp::Ordering::Greater
                } else if r1.is_subrange(&r2) {
                    std::cmp::Ordering::Less
                } else {
                    r1.start().cmp(&r2.start())
                }
            })
            .map(|(_ptr, scope)| *scope)
            .unwrap_or(original_scope)
    }

    pub fn resolve_local_name<'a>(&'a self, name_ref: ast::NameRef) -> Option<&'a ScopeEntry> {
        let mut shadowed = FxHashSet::default();
        let ret = self
            .scope_chain(name_ref.syntax())
            .flat_map(|scope| self.entries(scope).iter())
            .filter(|entry| shadowed.insert(entry.name()))
            .filter(|entry| entry.name() == &name_ref.text())
            .nth(0);
        ret
    }

    pub fn find_all_refs(&self, pat: ast::BindPat) -> Vec<ReferenceDescriptor> {
        let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
        let name_ptr = LocalSyntaxPtr::new(pat.syntax());
        let refs: Vec<_> = fn_def
            .syntax()
            .descendants()
            .filter_map(ast::NameRef::cast)
            .filter(|name_ref| match self.resolve_local_name(*name_ref) {
                None => false,
                Some(entry) => entry.ptr() == name_ptr,
            })
            .map(|name_ref| ReferenceDescriptor {
                name: name_ref.syntax().text().to_string(),
                range: name_ref.syntax().range(),
            })
            .collect();

        refs
    }

    fn root_scope(&mut self) -> ScopeId {
        self.scopes.alloc(ScopeData {
            parent: None,
            entries: vec![],
        })
    }
    fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
        self.scopes.alloc(ScopeData {
            parent: Some(parent),
            entries: vec![],
        })
    }
    fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) {
        let entries = pat
            .syntax()
            .descendants()
            .filter_map(ast::BindPat::cast)
            .filter_map(ScopeEntry::new);
        self.scopes[scope].entries.extend(entries);
    }
    fn add_params_bindings(&mut self, scope: ScopeId, params: Option<ast::ParamList>) {
        params
            .into_iter()
            .flat_map(|it| it.params())
            .filter_map(|it| it.pat())
            .for_each(|it| self.add_bindings(scope, it));
    }
    fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) {
        self.scope_for.insert(LocalSyntaxPtr::new(node), scope);
    }
    fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> {
        node.ancestors()
            .map(LocalSyntaxPtr::new)
            .filter_map(|it| self.scope_for.get(&it).map(|&scope| scope))
            .next()
    }
}

impl ScopeEntry {
    fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
        let name = pat.name()?;
        let res = ScopeEntry {
            name: name.text(),
            ptr: LocalSyntaxPtr::new(pat.syntax()),
        };
        Some(res)
    }
    pub fn name(&self) -> &SmolStr {
        &self.name
    }
    pub fn ptr(&self) -> LocalSyntaxPtr {
        self.ptr
    }
}

fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) {
    // A hack for completion :(
    scopes.set_scope(block.syntax(), scope);
    for stmt in block.statements() {
        match stmt {
            ast::Stmt::LetStmt(stmt) => {
                if let Some(expr) = stmt.initializer() {
                    scopes.set_scope(expr.syntax(), scope);
                    compute_expr_scopes(expr, scopes, scope);
                }
                scope = scopes.new_scope(scope);
                if let Some(pat) = stmt.pat() {
                    scopes.add_bindings(scope, pat);
                }
            }
            ast::Stmt::ExprStmt(expr_stmt) => {
                if let Some(expr) = expr_stmt.expr() {
                    scopes.set_scope(expr.syntax(), scope);
                    compute_expr_scopes(expr, scopes, scope);
                }
            }
        }
    }
    if let Some(expr) = block.expr() {
        eprintln!("{:?}", expr);
        scopes.set_scope(expr.syntax(), scope);
        compute_expr_scopes(expr, scopes, scope);
    }
}

fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) {
    match expr {
        ast::Expr::IfExpr(e) => {
            let cond_scope = e
                .condition()
                .and_then(|cond| compute_cond_scopes(cond, scopes, scope));
            if let Some(block) = e.then_branch() {
                compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
            }
            if let Some(block) = e.else_branch() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::BlockExpr(e) => {
            if let Some(block) = e.block() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::LoopExpr(e) => {
            if let Some(block) = e.loop_body() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::WhileExpr(e) => {
            let cond_scope = e
                .condition()
                .and_then(|cond| compute_cond_scopes(cond, scopes, scope));
            if let Some(block) = e.loop_body() {
                compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
            }
        }
        ast::Expr::ForExpr(e) => {
            if let Some(expr) = e.iterable() {
                compute_expr_scopes(expr, scopes, scope);
            }
            let mut scope = scope;
            if let Some(pat) = e.pat() {
                scope = scopes.new_scope(scope);
                scopes.add_bindings(scope, pat);
            }
            if let Some(block) = e.loop_body() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::LambdaExpr(e) => {
            let scope = scopes.new_scope(scope);
            scopes.add_params_bindings(scope, e.param_list());
            if let Some(body) = e.body() {
                scopes.set_scope(body.syntax(), scope);
                compute_expr_scopes(body, scopes, scope);
            }
        }
        ast::Expr::CallExpr(e) => {
            compute_call_scopes(e.expr(), e.arg_list(), scopes, scope);
        }
        ast::Expr::MethodCallExpr(e) => {
            compute_call_scopes(e.expr(), e.arg_list(), scopes, scope);
        }
        ast::Expr::MatchExpr(e) => {
            if let Some(expr) = e.expr() {
                compute_expr_scopes(expr, scopes, scope);
            }
            for arm in e.match_arm_list().into_iter().flat_map(|it| it.arms()) {
                let scope = scopes.new_scope(scope);
                for pat in arm.pats() {
                    scopes.add_bindings(scope, pat);
                }
                if let Some(expr) = arm.expr() {
                    compute_expr_scopes(expr, scopes, scope);
                }
            }
        }
        _ => expr
            .syntax()
            .children()
            .filter_map(ast::Expr::cast)
            .for_each(|expr| compute_expr_scopes(expr, scopes, scope)),
    };

    fn compute_call_scopes(
        receiver: Option<ast::Expr>,
        arg_list: Option<ast::ArgList>,
        scopes: &mut FnScopes,
        scope: ScopeId,
    ) {
        arg_list
            .into_iter()
            .flat_map(|it| it.args())
            .chain(receiver)
            .for_each(|expr| compute_expr_scopes(expr, scopes, scope));
    }

    fn compute_cond_scopes(
        cond: ast::Condition,
        scopes: &mut FnScopes,
        scope: ScopeId,
    ) -> Option<ScopeId> {
        if let Some(expr) = cond.expr() {
            compute_expr_scopes(expr, scopes, scope);
        }
        if let Some(pat) = cond.pat() {
            let s = scopes.new_scope(scope);
            scopes.add_bindings(s, pat);
            Some(s)
        } else {
            None
        }
    }
}

#[derive(Debug)]
pub struct ReferenceDescriptor {
    pub range: TextRange,
    pub name: String,
}

#[cfg(test)]
mod tests {
    use ra_editor::find_node_at_offset;
    use ra_syntax::SourceFileNode;
    use test_utils::extract_offset;

    use super::*;

    fn do_check(code: &str, expected: &[&str]) {
        let (off, code) = extract_offset(code);
        let code = {
            let mut buf = String::new();
            let off = u32::from(off) as usize;
            buf.push_str(&code[..off]);
            buf.push_str("marker");
            buf.push_str(&code[off..]);
            buf
        };
        let file = SourceFileNode::parse(&code);
        let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
        let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
        let scopes = FnScopes::new(fn_def);
        let actual = scopes
            .scope_chain(marker.syntax())
            .flat_map(|scope| scopes.entries(scope))
            .map(|it| it.name())
            .collect::<Vec<_>>();
        assert_eq!(actual.as_slice(), expected);
    }

    #[test]
    fn test_lambda_scope() {
        do_check(
            r"
            fn quux(foo: i32) {
                let f = |bar, baz: i32| {
                    <|>
                };
            }",
            &["bar", "baz", "foo"],
        );
    }

    #[test]
    fn test_call_scope() {
        do_check(
            r"
            fn quux() {
                f(|x| <|> );
            }",
            &["x"],
        );
    }

    #[test]
    fn test_metod_call_scope() {
        do_check(
            r"
            fn quux() {
                z.f(|x| <|> );
            }",
            &["x"],
        );
    }

    #[test]
    fn test_loop_scope() {
        do_check(
            r"
            fn quux() {
                loop {
                    let x = ();
                    <|>
                };
            }",
            &["x"],
        );
    }

    #[test]
    fn test_match() {
        do_check(
            r"
            fn quux() {
                match () {
                    Some(x) => {
                        <|>
                    }
                };
            }",
            &["x"],
        );
    }

    #[test]
    fn test_shadow_variable() {
        do_check(
            r"
            fn foo(x: String) {
                let x : &str = &x<|>;
            }",
            &["x"],
        );
    }

    fn do_check_local_name(code: &str, expected_offset: u32) {
        let (off, code) = extract_offset(code);
        let file = SourceFileNode::parse(&code);
        let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
        let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();

        let scopes = FnScopes::new(fn_def);

        let local_name_entry = scopes.resolve_local_name(name_ref).unwrap();
        let local_name = local_name_entry.ptr().resolve(&file);
        let expected_name =
            find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap();
        assert_eq!(local_name.range(), expected_name.syntax().range());
    }

    #[test]
    fn test_resolve_local_name() {
        do_check_local_name(
            r#"
            fn foo(x: i32, y: u32) {
                {
                    let z = x * 2;
                }
                {
                    let t = x<|> * 3;
                }
            }"#,
            21,
        );
    }

    #[test]
    fn test_resolve_local_name_declaration() {
        do_check_local_name(
            r#"
            fn foo(x: String) {
                let x : &str = &x<|>;
            }"#,
            21,
        );
    }

    #[test]
    fn test_resolve_local_name_shadow() {
        do_check_local_name(
            r"
        fn foo(x: String) {
            let x : &str = &x;
            x<|>
        }",
            46,
        );
    }
}
<commit_msg>debug pring<commit_after>use rustc_hash::{FxHashMap, FxHashSet};

use ra_syntax::{
    AstNode, SmolStr, SyntaxNodeRef, TextUnit, TextRange,
    algo::generate,
    ast::{self, ArgListOwner, LoopBodyOwner, NameOwner},
};
use ra_db::LocalSyntaxPtr;

use crate::{
    arena::{Arena, Id},
};

pub(crate) type ScopeId = Id<ScopeData>;

#[derive(Debug, PartialEq, Eq)]
pub struct FnScopes {
    pub self_param: Option<LocalSyntaxPtr>,
    scopes: Arena<ScopeData>,
    scope_for: FxHashMap<LocalSyntaxPtr, ScopeId>,
}

#[derive(Debug, PartialEq, Eq)]
pub struct ScopeEntry {
    name: SmolStr,
    ptr: LocalSyntaxPtr,
}

#[derive(Debug, PartialEq, Eq)]
pub struct ScopeData {
    parent: Option<ScopeId>,
    entries: Vec<ScopeEntry>,
}

impl FnScopes {
    pub fn new(fn_def: ast::FnDef) -> FnScopes {
        let mut scopes = FnScopes {
            self_param: fn_def
                .param_list()
                .and_then(|it| it.self_param())
                .map(|it| LocalSyntaxPtr::new(it.syntax())),
            scopes: Arena::default(),
            scope_for: FxHashMap::default(),
        };
        let root = scopes.root_scope();
        scopes.add_params_bindings(root, fn_def.param_list());
        if let Some(body) = fn_def.body() {
            compute_block_scopes(body, &mut scopes, root)
        }
        scopes
    }
    pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
        &self.scopes[scope].entries
    }
    pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a {
        generate(self.scope_for(node), move |&scope| {
            self.scopes[scope].parent
        })
    }
    pub fn scope_chain_for_offset<'a>(
        &'a self,
        offset: TextUnit,
    ) -> impl Iterator<Item = ScopeId> + 'a {
        let scope = self
            .scope_for
            .iter()
            // find containin scope
            .min_by_key(|(ptr, _scope)| {
                (
                    !(ptr.range().start() <= offset && offset <= ptr.range().end()),
                    ptr.range().len(),
                )
            })
            .map(|(ptr, scope)| self.adjust(*ptr, *scope, offset));

        generate(scope, move |&scope| self.scopes[scope].parent)
    }
    // XXX: during completion, cursor might be outside of any particular
    // expression. Try to figure out the correct scope...
    fn adjust(&self, ptr: LocalSyntaxPtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId {
        let r = ptr.range();
        let child_scopes = self
            .scope_for
            .iter()
            .map(|(ptr, scope)| (ptr.range(), scope))
            .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r);

        child_scopes
            .max_by(|(r1, _), (r2, _)| {
                if r2.is_subrange(&r1) {
                    std::cmp::Ordering::Greater
                } else if r1.is_subrange(&r2) {
                    std::cmp::Ordering::Less
                } else {
                    r1.start().cmp(&r2.start())
                }
            })
            .map(|(_ptr, scope)| *scope)
            .unwrap_or(original_scope)
    }

    pub fn resolve_local_name<'a>(&'a self, name_ref: ast::NameRef) -> Option<&'a ScopeEntry> {
        let mut shadowed = FxHashSet::default();
        let ret = self
            .scope_chain(name_ref.syntax())
            .flat_map(|scope| self.entries(scope).iter())
            .filter(|entry| shadowed.insert(entry.name()))
            .filter(|entry| entry.name() == &name_ref.text())
            .nth(0);
        ret
    }

    pub fn find_all_refs(&self, pat: ast::BindPat) -> Vec<ReferenceDescriptor> {
        let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
        let name_ptr = LocalSyntaxPtr::new(pat.syntax());
        let refs: Vec<_> = fn_def
            .syntax()
            .descendants()
            .filter_map(ast::NameRef::cast)
            .filter(|name_ref| match self.resolve_local_name(*name_ref) {
                None => false,
                Some(entry) => entry.ptr() == name_ptr,
            })
            .map(|name_ref| ReferenceDescriptor {
                name: name_ref.syntax().text().to_string(),
                range: name_ref.syntax().range(),
            })
            .collect();

        refs
    }

    fn root_scope(&mut self) -> ScopeId {
        self.scopes.alloc(ScopeData {
            parent: None,
            entries: vec![],
        })
    }
    fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
        self.scopes.alloc(ScopeData {
            parent: Some(parent),
            entries: vec![],
        })
    }
    fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) {
        let entries = pat
            .syntax()
            .descendants()
            .filter_map(ast::BindPat::cast)
            .filter_map(ScopeEntry::new);
        self.scopes[scope].entries.extend(entries);
    }
    fn add_params_bindings(&mut self, scope: ScopeId, params: Option<ast::ParamList>) {
        params
            .into_iter()
            .flat_map(|it| it.params())
            .filter_map(|it| it.pat())
            .for_each(|it| self.add_bindings(scope, it));
    }
    fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) {
        self.scope_for.insert(LocalSyntaxPtr::new(node), scope);
    }
    fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> {
        node.ancestors()
            .map(LocalSyntaxPtr::new)
            .filter_map(|it| self.scope_for.get(&it).map(|&scope| scope))
            .next()
    }
}

impl ScopeEntry {
    fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
        let name = pat.name()?;
        let res = ScopeEntry {
            name: name.text(),
            ptr: LocalSyntaxPtr::new(pat.syntax()),
        };
        Some(res)
    }
    pub fn name(&self) -> &SmolStr {
        &self.name
    }
    pub fn ptr(&self) -> LocalSyntaxPtr {
        self.ptr
    }
}

fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) {
    // A hack for completion :(
    scopes.set_scope(block.syntax(), scope);
    for stmt in block.statements() {
        match stmt {
            ast::Stmt::LetStmt(stmt) => {
                if let Some(expr) = stmt.initializer() {
                    scopes.set_scope(expr.syntax(), scope);
                    compute_expr_scopes(expr, scopes, scope);
                }
                scope = scopes.new_scope(scope);
                if let Some(pat) = stmt.pat() {
                    scopes.add_bindings(scope, pat);
                }
            }
            ast::Stmt::ExprStmt(expr_stmt) => {
                if let Some(expr) = expr_stmt.expr() {
                    scopes.set_scope(expr.syntax(), scope);
                    compute_expr_scopes(expr, scopes, scope);
                }
            }
        }
    }
    if let Some(expr) = block.expr() {
        scopes.set_scope(expr.syntax(), scope);
        compute_expr_scopes(expr, scopes, scope);
    }
}

fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) {
    match expr {
        ast::Expr::IfExpr(e) => {
            let cond_scope = e
                .condition()
                .and_then(|cond| compute_cond_scopes(cond, scopes, scope));
            if let Some(block) = e.then_branch() {
                compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
            }
            if let Some(block) = e.else_branch() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::BlockExpr(e) => {
            if let Some(block) = e.block() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::LoopExpr(e) => {
            if let Some(block) = e.loop_body() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::WhileExpr(e) => {
            let cond_scope = e
                .condition()
                .and_then(|cond| compute_cond_scopes(cond, scopes, scope));
            if let Some(block) = e.loop_body() {
                compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
            }
        }
        ast::Expr::ForExpr(e) => {
            if let Some(expr) = e.iterable() {
                compute_expr_scopes(expr, scopes, scope);
            }
            let mut scope = scope;
            if let Some(pat) = e.pat() {
                scope = scopes.new_scope(scope);
                scopes.add_bindings(scope, pat);
            }
            if let Some(block) = e.loop_body() {
                compute_block_scopes(block, scopes, scope);
            }
        }
        ast::Expr::LambdaExpr(e) => {
            let scope = scopes.new_scope(scope);
            scopes.add_params_bindings(scope, e.param_list());
            if let Some(body) = e.body() {
                scopes.set_scope(body.syntax(), scope);
                compute_expr_scopes(body, scopes, scope);
            }
        }
        ast::Expr::CallExpr(e) => {
            compute_call_scopes(e.expr(), e.arg_list(), scopes, scope);
        }
        ast::Expr::MethodCallExpr(e) => {
            compute_call_scopes(e.expr(), e.arg_list(), scopes, scope);
        }
        ast::Expr::MatchExpr(e) => {
            if let Some(expr) = e.expr() {
                compute_expr_scopes(expr, scopes, scope);
            }
            for arm in e.match_arm_list().into_iter().flat_map(|it| it.arms()) {
                let scope = scopes.new_scope(scope);
                for pat in arm.pats() {
                    scopes.add_bindings(scope, pat);
                }
                if let Some(expr) = arm.expr() {
                    compute_expr_scopes(expr, scopes, scope);
                }
            }
        }
        _ => expr
            .syntax()
            .children()
            .filter_map(ast::Expr::cast)
            .for_each(|expr| compute_expr_scopes(expr, scopes, scope)),
    };

    fn compute_call_scopes(
        receiver: Option<ast::Expr>,
        arg_list: Option<ast::ArgList>,
        scopes: &mut FnScopes,
        scope: ScopeId,
    ) {
        arg_list
            .into_iter()
            .flat_map(|it| it.args())
            .chain(receiver)
            .for_each(|expr| compute_expr_scopes(expr, scopes, scope));
    }

    fn compute_cond_scopes(
        cond: ast::Condition,
        scopes: &mut FnScopes,
        scope: ScopeId,
    ) -> Option<ScopeId> {
        if let Some(expr) = cond.expr() {
            compute_expr_scopes(expr, scopes, scope);
        }
        if let Some(pat) = cond.pat() {
            let s = scopes.new_scope(scope);
            scopes.add_bindings(s, pat);
            Some(s)
        } else {
            None
        }
    }
}

#[derive(Debug)]
pub struct ReferenceDescriptor {
    pub range: TextRange,
    pub name: String,
}

#[cfg(test)]
mod tests {
    use ra_editor::find_node_at_offset;
    use ra_syntax::SourceFileNode;
    use test_utils::extract_offset;

    use super::*;

    fn do_check(code: &str, expected: &[&str]) {
        let (off, code) = extract_offset(code);
        let code = {
            let mut buf = String::new();
            let off = u32::from(off) as usize;
            buf.push_str(&code[..off]);
            buf.push_str("marker");
            buf.push_str(&code[off..]);
            buf
        };
        let file = SourceFileNode::parse(&code);
        let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
        let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
        let scopes = FnScopes::new(fn_def);
        let actual = scopes
            .scope_chain(marker.syntax())
            .flat_map(|scope| scopes.entries(scope))
            .map(|it| it.name())
            .collect::<Vec<_>>();
        assert_eq!(actual.as_slice(), expected);
    }

    #[test]
    fn test_lambda_scope() {
        do_check(
            r"
            fn quux(foo: i32) {
                let f = |bar, baz: i32| {
                    <|>
                };
            }",
            &["bar", "baz", "foo"],
        );
    }

    #[test]
    fn test_call_scope() {
        do_check(
            r"
            fn quux() {
                f(|x| <|> );
            }",
            &["x"],
        );
    }

    #[test]
    fn test_metod_call_scope() {
        do_check(
            r"
            fn quux() {
                z.f(|x| <|> );
            }",
            &["x"],
        );
    }

    #[test]
    fn test_loop_scope() {
        do_check(
            r"
            fn quux() {
                loop {
                    let x = ();
                    <|>
                };
            }",
            &["x"],
        );
    }

    #[test]
    fn test_match() {
        do_check(
            r"
            fn quux() {
                match () {
                    Some(x) => {
                        <|>
                    }
                };
            }",
            &["x"],
        );
    }

    #[test]
    fn test_shadow_variable() {
        do_check(
            r"
            fn foo(x: String) {
                let x : &str = &x<|>;
            }",
            &["x"],
        );
    }

    fn do_check_local_name(code: &str, expected_offset: u32) {
        let (off, code) = extract_offset(code);
        let file = SourceFileNode::parse(&code);
        let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
        let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();

        let scopes = FnScopes::new(fn_def);

        let local_name_entry = scopes.resolve_local_name(name_ref).unwrap();
        let local_name = local_name_entry.ptr().resolve(&file);
        let expected_name =
            find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap();
        assert_eq!(local_name.range(), expected_name.syntax().range());
    }

    #[test]
    fn test_resolve_local_name() {
        do_check_local_name(
            r#"
            fn foo(x: i32, y: u32) {
                {
                    let z = x * 2;
                }
                {
                    let t = x<|> * 3;
                }
            }"#,
            21,
        );
    }

    #[test]
    fn test_resolve_local_name_declaration() {
        do_check_local_name(
            r#"
            fn foo(x: String) {
                let x : &str = &x<|>;
            }"#,
            21,
        );
    }

    #[test]
    fn test_resolve_local_name_shadow() {
        do_check_local_name(
            r"
        fn foo(x: String) {
            let x : &str = &x;
            x<|>
        }",
            46,
        );
    }
}
<|endoftext|>


<commit_before>use stdweb::{
    js,
    web::{window, CanvasRenderingContext2d, FillRule},
};

// pub use crate::image::Image as InnerImage;
use crate::{utils::*, FontConfig, RenderPipeline, RenderTarget, TextMetrics};

pub use self::image::*;

mod image;

/// The RenderContext trait, provides the rendering context. It is used for drawing shapes, text, images, and other objects.
pub struct RenderContext {
    canvas_render_context_2_d: CanvasRenderingContext2d,
    font_config: FontConfig,
}

impl RenderContext {
    /// Creates a new render context 2d.
    pub fn new(canvas_render_context_2_d: CanvasRenderingContext2d) -> Self {
        canvas_render_context_2_d.set_text_baseline(stdweb::web::TextBaseline::Middle);
        RenderContext {
            canvas_render_context_2_d,
            font_config: FontConfig::default(),
        }
    }

    // Rectangles

    /// Draws a filled rectangle whose starting point is at the coordinates {x, y} with the specified width and height and whose style is determined by the fillStyle attribute.
    pub fn fill_rect(&mut self, x: f64, y: f64, width: f64, height: f64) {
        self.canvas_render_context_2_d
            .fill_rect(x, y, width, height);
    }

    /// Draws a rectangle that is stroked (outlined) according to the current strokeStyle and other context settings.
    pub fn stroke_rect(&mut self, x: f64, y: f64, width: f64, height: f64) {
        self.canvas_render_context_2_d
            .stroke_rect(x, y, width, height);
    }

    // Text

    /// Draws (fills) a given text at the given (x, y) position.
    pub fn fill_text(&mut self, text: &str, x: f64, y: f64) {
        self.canvas_render_context_2_d
            .set_text_baseline(stdweb::web::TextBaseline::Middle);
        self.canvas_render_context_2_d.fill_text(
            text,
            x,
            y + self.font_config.font_size.ceil() / 2.0,
            None,
        );
    }

    pub fn measure(
        &mut self,
        text: &str,
        font_size: f64,
        family: impl Into<String>,
    ) -> TextMetrics {
        self.set_font_family(family);
        self.set_font_size(font_size);
        self.measure_text(text)
    }

    /// Returns a TextMetrics object.
    pub fn measure_text(&mut self, text: &str) -> TextMetrics {
        TextMetrics {
            width: self
                .canvas_render_context_2_d
                .measure_text(text)
                .unwrap()
                .get_width(),
            height: self.font_config.font_size.ceil(),
        }
    }

    /// Fills the current or given path with the current file style.
    pub fn fill(&mut self) {
        self.canvas_render_context_2_d.fill(FillRule::default());
    }

    /// Strokes {outlines} the current or given path with the current stroke style.
    pub fn stroke(&mut self) {
        self.canvas_render_context_2_d.stroke();
    }

    /// Starts a new path by emptying the list of sub-paths. Call this when you want to create a new path.
    pub fn begin_path(&mut self) {
        self.canvas_render_context_2_d.begin_path();
    }

    /// Attempts to add a straight line from the current point to the start of the current sub-path. If the shape has already been closed or has only one point, this function does nothing.
    pub fn close_path(&mut self) {
        self.canvas_render_context_2_d.close_path();
    }

    /// Adds a rectangle to the current path.
    pub fn rect(&mut self, x: f64, y: f64, width: f64, height: f64) {
        self.canvas_render_context_2_d.rect(x, y, width, height);
    }

    /// Creates a circular arc centered at (x, y) with a radius of radius. The path starts at startAngle and ends at endAngle.
    pub fn arc(&mut self, x: f64, y: f64, radius: f64, start_angle: f64, end_angle: f64) {
        self.canvas_render_context_2_d
            .arc(x, y, radius, start_angle, end_angle, false);
    }

    /// Begins a new sub-path at the point specified by the given {x, y} coordinates.

    pub fn move_to(&mut self, x: f64, y: f64) {
        self.canvas_render_context_2_d.move_to(x, y);
    }

    /// Adds a straight line to the current sub-path by connecting the sub-path's last point to the specified {x, y} coordinates.
    pub fn line_to(&mut self, x: f64, y: f64) {
        self.canvas_render_context_2_d.line_to(x, y);
    }

    /// Adds a quadratic Bézier curve to the current sub-path.
    pub fn quadratic_curve_to(&mut self, cpx: f64, cpy: f64, x: f64, y: f64) {
        self.canvas_render_context_2_d
            .quadratic_curve_to(cpx, cpy, x, y);
    }

    /// Adds a cubic Bézier curve to the current sub-path. It requires three points: the first two are control points and the third one is the end point. The starting point is the latest point in the current path, which can be changed using MoveTo{} before creating the Bézier curve.
    pub fn bezier_curve_to(&mut self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) {
        self.canvas_render_context_2_d
            .bezier_curve_to(cp1x, cp1y, cp2x, cp2y, x, y);
    }

    // Draw image

    /// Draws the image.
    pub fn draw_image(&mut self, image: &Image, x: f64, y: f64) {
        js!(
            var img = document.image_store.image(@{&image.source});

            if(img == null) {
                img = document.image_store.load_image(@{&image.source});
                img.then(
                    function(i) {
                         @{&self.canvas_render_context_2_d}.drawImage(i, @{&x}, @{&y});
                    }
                )
            } else {
                //  @{&self.canvas_render_context_2_d}.drawImage(img, @{&x}, @{&y});
            }
        );
    }

    /// Draws the image with the given size.
    pub fn draw_image_with_size(&mut self, image: &Image, x: f64, y: f64, width: f64, height: f64) {
        js!(
            var img = document.image_store.image(@{&image.source});

            if(img == null) {
                img = document.image_store.load_image(@{&image.source});

                img.then(
                    function(i) {
                         @{&self.canvas_render_context_2_d}.drawImage(i, @{&x}, @{&y}, @{&width}, @{&height});
                    }
                )
            } else {
                 @{&self.canvas_render_context_2_d}.drawImage(img, @{&x}, @{&y});
            }
        );
    }

    /// Draws the given part of the image.
    pub fn draw_image_with_clip_and_size(
        &mut self,
        image: &Image,
        clip_x: f64,
        clip_y: f64,
        clip_width: f64,
        clip_height: f64,
        x: f64,
        y: f64,
        width: f64,
        height: f64,
    ) {
        js!(
            var img = document.image_store.image(@{&image.source});

            if(img == null) {
                img = document.image_store.load_image(@{&image.source});
                img.then(
                    function(i) {
                         @{&self.canvas_render_context_2_d}.drawImage(img, @{&clip_x}, @{&clip_y}, @{&clip_width}, @{&clip_height}, @{&x}, @{&y}, @{&width}, @{&height});
                    }
                )
            } else {
                 @{&self.canvas_render_context_2_d}.drawImage(img, @{&x}, @{&y});
            }
        );
    }

    pub fn draw_pipeline(
        &mut self,
        x: f64,
        y: f64,
        width: f64,
        height: f64,
        pipeline: Box<dyn RenderPipeline>,
    ) {
        let mut render_target = RenderTarget::new(width as u32, height as u32);
        pipeline.draw_pipeline(&mut render_target);

        let device_pixel_ratio = window().device_pixel_ratio();
        // let device_pixel_ratio = 1.0;

        let image_data = self
            .canvas_render_context_2_d
            .create_image_data(width, height)
            .unwrap();

        for i in 0..(render_target.data.len() - 1) {
            let pixel = render_target.data.get(i).unwrap();
            let r = ((pixel & 0x00FF0000) >> 16) as u8;
            let g = ((pixel & 0x0000FF00) >> 8) as u8;
            let b = (pixel & 0x000000FF) as u8;
            let a = ((pixel & 0xFF000000) >> 24) as u8;

            let index = i as u32 * 4;
            js!(
                @{&image_data}.data[@{index} + 0] = @{r};  // R value
                @{&image_data}.data[@{index} + 1] = @{g};    // G value
                @{&image_data}.data[@{index} + 2] = @{b};  // B value
                @{&image_data}.data[@{index} + 3] = @{a};  // A value
            );
        }

        js!(
            var tempCanvas=document.createElement("canvas");
            var tempCtx=tempCanvas.getContext("2d");
            var x = @{&x};
            var y = @{&y};

            // set the temp canvas size == the canvas size
            tempCanvas.width=@{&width};
            tempCanvas.height=@{&height};

            // put the modified pixels on the temp canvas
            tempCtx.putImageData(@{&image_data},0,0);
            // tempCtx.scale(10 * @{&device_pixel_ratio}, 10 * @{&device_pixel_ratio});

            // use the tempCanvas.toDataURL to create an img object
            var img=new Image();
            img.src=tempCanvas.toDataURL();
            img.onload=function(){
                // drawImage the img on the canvas
                @{&self.canvas_render_context_2_d}.drawImage(img,x,y);
            }
            // console.log("Test")
            //
        );
    }

    /// Creates a clipping path from the current sub-paths. Everything drawn after clip() is called appears inside the clipping path only.
    pub fn clip(&mut self) {
        self.canvas_render_context_2_d.clip(FillRule::EvenOdd);
    }

    // Line styles

    /// Sets the thickness of lines.
    pub fn set_line_width(&mut self, line_width: f64) {
        self.canvas_render_context_2_d.set_line_width(line_width);
    }

    /// Specific the font family.
    pub fn set_font_family(&mut self, family: impl Into<String>) {
        self.font_config.family = family.into().replace(" Regular", "");
        self.canvas_render_context_2_d
            .set_font(&self.font_config.to_string());
    }

    /// Specifies the font size.
    pub fn set_font_size(&mut self, size: f64) {
        self.font_config.font_size = size;
        self.canvas_render_context_2_d
            .set_font(&self.font_config.to_string());
    }

    // Fill and stroke style

    /// Specifies the fill color to use inside shapes.
    pub fn set_fill_style(&mut self, brush: Brush) {
        match brush {
            Brush::SolidColor(color) => {
                self.canvas_render_context_2_d
                    .set_fill_style_color(&color.to_string());
            }
            _ => (),
        }
    }

    /// Specifies the fill stroke to use inside shapes.
    pub fn set_stroke_style(&mut self, brush: Brush) {
        match brush {
            Brush::SolidColor(color) => {
                self.canvas_render_context_2_d
                    .set_stroke_style_color(&color.to_string());
            }
            _ => (),
        }
    }

    // Transformations

    /// Sets the tranformation.
    pub fn set_transform(&mut self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) {
        self.canvas_render_context_2_d
            .set_transform(a, b, c, d, e, f);
    }

    // Canvas states

    /// Saves the entire state of the canvas by pushing the current state onto a stack.
    pub fn save(&mut self) {
        self.canvas_render_context_2_d.save();
    }

    /// Restores the most recently saved canvas state by popping the top entry in the drawing state stack. If there is no saved state, this method does nothing.
    pub fn restore(&mut self) {
        self.canvas_render_context_2_d.restore();
    }

    pub fn clear(&mut self, brush: &Brush) {
        let color = match brush {
            Brush::SolidColor(color) => color.to_string(),
            _ => Color::rgba(0, 0, 0, 0).to_string(),
        };

        self.save();
        self.canvas_render_context_2_d
            .set_fill_style_color(color.as_str());
        let canvas = self.canvas_render_context_2_d.get_canvas();
        self.canvas_render_context_2_d.fill_rect(
            0.0,
            0.0,
            canvas.width() as f64,
            canvas.height() as f64,
        );
        self.restore();
    }

    pub fn set_canvas_render_context_2d(
        &mut self,
        canvas_render_context_2_d: CanvasRenderingContext2d,
    ) {
        self.canvas_render_context_2_d = canvas_render_context_2_d;
    }

    pub fn start(&mut self) {}
    pub fn finish(&mut self) {}
}

// --- Conversions ---

impl From<&str> for Image {
    fn from(s: &str) -> Image {
        Image::new(s)
    }
}

impl From<String> for Image {
    fn from(s: String) -> Image {
        Image::new(s)
    }
}
<commit_msg>fix web canvas scale.<commit_after>use stdweb::{
    js,
    web::{window, CanvasRenderingContext2d, FillRule},
};

// pub use crate::image::Image as InnerImage;
use crate::{utils::*, FontConfig, RenderPipeline, RenderTarget, TextMetrics};

pub use self::image::*;

mod image;

/// The RenderContext trait, provides the rendering context. It is used for drawing shapes, text, images, and other objects.
pub struct RenderContext {
    canvas_render_context_2_d: CanvasRenderingContext2d,
    font_config: FontConfig,
}

impl RenderContext {
    /// Creates a new render context 2d.
    pub fn new(canvas_render_context_2_d: CanvasRenderingContext2d) -> Self {
        canvas_render_context_2_d.set_text_baseline(stdweb::web::TextBaseline::Middle);
        RenderContext {
            canvas_render_context_2_d,
            font_config: FontConfig::default(),
        }
    }

    // Rectangles

    /// Draws a filled rectangle whose starting point is at the coordinates {x, y} with the specified width and height and whose style is determined by the fillStyle attribute.
    pub fn fill_rect(&mut self, x: f64, y: f64, width: f64, height: f64) {
        self.canvas_render_context_2_d
            .fill_rect(x, y, width, height);
    }

    /// Draws a rectangle that is stroked (outlined) according to the current strokeStyle and other context settings.
    pub fn stroke_rect(&mut self, x: f64, y: f64, width: f64, height: f64) {
        self.canvas_render_context_2_d
            .stroke_rect(x, y, width, height);
    }

    // Text

    /// Draws (fills) a given text at the given (x, y) position.
    pub fn fill_text(&mut self, text: &str, x: f64, y: f64) {
        self.canvas_render_context_2_d
            .set_text_baseline(stdweb::web::TextBaseline::Middle);
        self.canvas_render_context_2_d.fill_text(
            text,
            x,
            y + self.font_config.font_size.ceil() / 2.0,
            None,
        );
    }

    pub fn measure(
        &mut self,
        text: &str,
        font_size: f64,
        family: impl Into<String>,
    ) -> TextMetrics {
        self.set_font_family(family);
        self.set_font_size(font_size);
        self.measure_text(text)
    }

    /// Returns a TextMetrics object.
    pub fn measure_text(&mut self, text: &str) -> TextMetrics {
        TextMetrics {
            width: self
                .canvas_render_context_2_d
                .measure_text(text)
                .unwrap()
                .get_width(),
            height: self.font_config.font_size.ceil(),
        }
    }

    /// Fills the current or given path with the current file style.
    pub fn fill(&mut self) {
        self.canvas_render_context_2_d.fill(FillRule::default());
    }

    /// Strokes {outlines} the current or given path with the current stroke style.
    pub fn stroke(&mut self) {
        self.canvas_render_context_2_d.stroke();
    }

    /// Starts a new path by emptying the list of sub-paths. Call this when you want to create a new path.
    pub fn begin_path(&mut self) {
        self.canvas_render_context_2_d.begin_path();
    }

    /// Attempts to add a straight line from the current point to the start of the current sub-path. If the shape has already been closed or has only one point, this function does nothing.
    pub fn close_path(&mut self) {
        self.canvas_render_context_2_d.close_path();
    }

    /// Adds a rectangle to the current path.
    pub fn rect(&mut self, x: f64, y: f64, width: f64, height: f64) {
        self.canvas_render_context_2_d.rect(x, y, width, height);
    }

    /// Creates a circular arc centered at (x, y) with a radius of radius. The path starts at startAngle and ends at endAngle.
    pub fn arc(&mut self, x: f64, y: f64, radius: f64, start_angle: f64, end_angle: f64) {
        self.canvas_render_context_2_d
            .arc(x, y, radius, start_angle, end_angle, false);
    }

    /// Begins a new sub-path at the point specified by the given {x, y} coordinates.

    pub fn move_to(&mut self, x: f64, y: f64) {
        self.canvas_render_context_2_d.move_to(x, y);
    }

    /// Adds a straight line to the current sub-path by connecting the sub-path's last point to the specified {x, y} coordinates.
    pub fn line_to(&mut self, x: f64, y: f64) {
        self.canvas_render_context_2_d.line_to(x, y);
    }

    /// Adds a quadratic Bézier curve to the current sub-path.
    pub fn quadratic_curve_to(&mut self, cpx: f64, cpy: f64, x: f64, y: f64) {
        self.canvas_render_context_2_d
            .quadratic_curve_to(cpx, cpy, x, y);
    }

    /// Adds a cubic Bézier curve to the current sub-path. It requires three points: the first two are control points and the third one is the end point. The starting point is the latest point in the current path, which can be changed using MoveTo{} before creating the Bézier curve.
    pub fn bezier_curve_to(&mut self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) {
        self.canvas_render_context_2_d
            .bezier_curve_to(cp1x, cp1y, cp2x, cp2y, x, y);
    }

    // Draw image

    /// Draws the image.
    pub fn draw_image(&mut self, image: &Image, x: f64, y: f64) {
        js!(
            var img = document.image_store.image(@{&image.source});

            if(img == null) {
                img = document.image_store.load_image(@{&image.source});
                img.then(
                    function(i) {
                         @{&self.canvas_render_context_2_d}.drawImage(i, @{&x}, @{&y});
                    }
                )
            } else {
                //  @{&self.canvas_render_context_2_d}.drawImage(img, @{&x}, @{&y});
            }
        );
    }

    /// Draws the image with the given size.
    pub fn draw_image_with_size(&mut self, image: &Image, x: f64, y: f64, width: f64, height: f64) {
        js!(
            var img = document.image_store.image(@{&image.source});

            if(img == null) {
                img = document.image_store.load_image(@{&image.source});

                img.then(
                    function(i) {
                         @{&self.canvas_render_context_2_d}.drawImage(i, @{&x}, @{&y}, @{&width}, @{&height});
                    }
                )
            } else {
                 @{&self.canvas_render_context_2_d}.drawImage(img, @{&x}, @{&y});
            }
        );
    }

    /// Draws the given part of the image.
    pub fn draw_image_with_clip_and_size(
        &mut self,
        image: &Image,
        clip_x: f64,
        clip_y: f64,
        clip_width: f64,
        clip_height: f64,
        x: f64,
        y: f64,
        width: f64,
        height: f64,
    ) {
        js!(
            var img = document.image_store.image(@{&image.source});

            if(img == null) {
                img = document.image_store.load_image(@{&image.source});
                img.then(
                    function(i) {
                         @{&self.canvas_render_context_2_d}.drawImage(img, @{&clip_x}, @{&clip_y}, @{&clip_width}, @{&clip_height}, @{&x}, @{&y}, @{&width}, @{&height});
                    }
                )
            } else {
                 @{&self.canvas_render_context_2_d}.drawImage(img, @{&x}, @{&y});
            }
        );
    }

    pub fn draw_pipeline(
        &mut self,
        x: f64,
        y: f64,
        width: f64,
        height: f64,
        pipeline: Box<dyn RenderPipeline>,
    ) {
        let mut render_target = RenderTarget::new(width as u32, height as u32);
        pipeline.draw_pipeline(&mut render_target);

        let image_data = self
            .canvas_render_context_2_d
            .create_image_data(width, height)
            .unwrap();

        for i in 0..(render_target.data.len() - 1) {
            let pixel = render_target.data.get(i).unwrap();
            let r = ((pixel & 0x00FF0000) >> 16) as u8;
            let g = ((pixel & 0x0000FF00) >> 8) as u8;
            let b = (pixel & 0x000000FF) as u8;
            let a = ((pixel & 0xFF000000) >> 24) as u8;

            let index = i as u32 * 4;
            js!(
                @{&image_data}.data[@{index} + 0] = @{r};  // R value
                @{&image_data}.data[@{index} + 1] = @{g};    // G value
                @{&image_data}.data[@{index} + 2] = @{b};  // B value
                @{&image_data}.data[@{index} + 3] = @{a};  // A value
            );
        }

        js!(
            var tempCanvas=document.createElement("canvas");
            var tempCtx=tempCanvas.getContext("2d");
            var x = @{&x};
            var y = @{&y};

            // set the temp canvas size == the canvas size
            tempCanvas.width=@{&width};
            tempCanvas.height=@{&height};

            // put the modified pixels on the temp canvas
            tempCtx.putImageData(@{&image_data},0,0);
            // tempCtx.scale(10 * @{&device_pixel_ratio}, 10 * @{&device_pixel_ratio});

            // use the tempCanvas.toDataURL to create an img object
            var img=new Image();
            img.src=tempCanvas.toDataURL();
            img.onload=function(){
                // drawImage the img on the canvas
                @{&self.canvas_render_context_2_d}.drawImage(img,x,y);
            }
        );
    }

    /// Creates a clipping path from the current sub-paths. Everything drawn after clip() is called appears inside the clipping path only.
    pub fn clip(&mut self) {
        self.canvas_render_context_2_d.clip(FillRule::EvenOdd);
    }

    // Line styles

    /// Sets the thickness of lines.
    pub fn set_line_width(&mut self, line_width: f64) {
        self.canvas_render_context_2_d.set_line_width(line_width);
    }

    /// Specific the font family.
    pub fn set_font_family(&mut self, family: impl Into<String>) {
        self.font_config.family = family.into().replace(" Regular", "");
        self.canvas_render_context_2_d
            .set_font(&self.font_config.to_string());
    }

    /// Specifies the font size.
    pub fn set_font_size(&mut self, size: f64) {
        self.font_config.font_size = size;
        self.canvas_render_context_2_d
            .set_font(&self.font_config.to_string());
    }

    // Fill and stroke style

    /// Specifies the fill color to use inside shapes.
    pub fn set_fill_style(&mut self, brush: Brush) {
        match brush {
            Brush::SolidColor(color) => {
                self.canvas_render_context_2_d
                    .set_fill_style_color(&color.to_string());
            }
            _ => (),
        }
    }

    /// Specifies the fill stroke to use inside shapes.
    pub fn set_stroke_style(&mut self, brush: Brush) {
        match brush {
            Brush::SolidColor(color) => {
                self.canvas_render_context_2_d
                    .set_stroke_style_color(&color.to_string());
            }
            _ => (),
        }
    }

    // Transformations

    /// Sets the tranformation.
    pub fn set_transform(&mut self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) {
        self.canvas_render_context_2_d
            .set_transform(a, b, c, d, e, f);
    }

    // Canvas states

    /// Saves the entire state of the canvas by pushing the current state onto a stack.
    pub fn save(&mut self) {
        self.canvas_render_context_2_d.save();
    }

    /// Restores the most recently saved canvas state by popping the top entry in the drawing state stack. If there is no saved state, this method does nothing.
    pub fn restore(&mut self) {
        self.canvas_render_context_2_d.restore();
    }

    pub fn clear(&mut self, brush: &Brush) {
        let color = match brush {
            Brush::SolidColor(color) => color.to_string(),
            _ => Color::rgba(0, 0, 0, 0).to_string(),
        };

        self.save();
        self.canvas_render_context_2_d
            .set_fill_style_color(color.as_str());
        let canvas = self.canvas_render_context_2_d.get_canvas();
        self.canvas_render_context_2_d.fill_rect(
            0.0,
            0.0,
            canvas.width() as f64,
            canvas.height() as f64,
        );
        self.restore();
    }

    pub fn set_canvas_render_context_2d(
        &mut self,
        canvas_render_context_2_d: CanvasRenderingContext2d,
    ) {
        self.canvas_render_context_2_d = canvas_render_context_2_d;
    }

    pub fn start(&mut self) {}
    pub fn finish(&mut self) {}
}

// --- Conversions ---

impl From<&str> for Image {
    fn from(s: &str) -> Image {
        Image::new(s)
    }
}

impl From<String> for Image {
    fn from(s: String) -> Image {
        Image::new(s)
    }
}
<|endoftext|>


<commit_before>#![feature(exclusive_range_pattern)]
fn read_string(a: String) -> Vec<u8> {
    a.bytes().collect()
}

fn fill_data(a: &mut Vec<u8>) {
    //fill data to N * 512 bits + 448 bits
    //N * 64 * 8 + 56 * 8
    let length = a.len();
    let module = length % 64;
    match module {
        0..56 => {
            a.push(0b1000_0000u8);
            let left_bytes = 55 - module;
            for _ in 0..left_bytes {
                a.push(0b0000_0000u8)
            }
        }
        57..=63 => {
            a.push(0b1000_0000u8);
            let left_bytes = 63 - module + 56;
            for _ in 0..left_bytes {
                a.push(0b0000_0000u8)
            }
        }
        _ => {}
    }

    //then give length of this data
    let mut add_length = ((length * 8) as u64).to_be_bytes().to_vec();
    a.append(&mut add_length);
}

fn function_F(x: &[u8; 4], y: &[u8; 4], z: &[u8; 4]) -> [u8; 4] {
    let mut temp0: [u8; 4] = [0, 0, 0, 0];
    for i in 0..4 {
        temp0[i] = x[i] & y[i];
    }
    dbg!(temp0);
    dbg!(x);

    let mut temp1: [u8; 4] = [0, 0, 0, 0];
    for i in 0..4 {
        temp1[i] = !x[i] & z[i];
    }
    dbg!(temp1);

    let mut temp2: [u8; 4] = [0, 0, 0, 0];
    for i in 0..4 {
        temp2[i] = temp0[i] | temp1[i];
    }

    temp2
}

fn function_G(x: &[u8; 4], y: &[u8; 4], z: &[u8; 4]) -> [u8; 4] {
    let mut temp0: [u8; 4] = [0, 0, 0, 0];
    for i in 0..4 {
        temp0[i] = x[i] & z[i];
    }
    dbg!(temp0);
    dbg!(x);

    let mut temp1: [u8; 4] = [0, 0, 0, 0];
    for i in 0..4 {
        temp1[i] = y[i] & !z[i];
    }
    dbg!(temp1);

    let mut temp2: [u8; 4] = [0, 0, 0, 0];
    for i in 0..4 {
        temp2[i] = temp0[i] | temp1[i];
    }

    temp2
}

fn function_H(x: &[u8; 4], y: &[u8; 4], z: &[u8; 4]) -> [u8; 4] {
    let mut temp0: [u8; 4] = [0, 0, 0, 0];
    for i in 0..4 {
        temp0[i] = x[i] ^ y[i] ^ z[i];
    }
    temp0
}

fn function_I(x: &[u8; 4], y: &[u8; 4], z: &[u8; 4]) -> [u8; 4] {
    let mut temp0: [u8; 4] = [0, 0, 0, 0];
    for i in 0..4 {
        temp0[i] = y[i] ^ (x[i] | !z[i]);
    }
    temp0
}

fn bits_plus(x: &[u8; 4], y: &[u8; 4]) -> [u8; 4] {
    let mut result: [u8; 4] = [0, 0, 0, 0];

    for i in 0..4 {
        result[i] = x[i] + y[i]
    }

    result
}

//:= TODO
//fn offset_func

//:= todo
fn wrap_function<FF>(
    a: &mut [u8; 4],
    b: &[u8; 4],
    c: &[u8; 4],
    d: &[u8; 4],
    mj: &[u8; 4],
    s: &[u8; 4],
    ti: &[u8; 4],
    fu: FF,
) where
    FF: Fn(&[u8; 4], &[u8; 4], &[u8; 4]) -> [u8; 4],
{
}

fn main() {
    // let mut a = vec![0b1011_0000u8];
    // fill_data(&mut a);
    // println!("{:?}", a.len());

    let mut b = [0b1001_1001u8; 67].to_vec();
    fill_data(&mut b);
    println!("{:?}", b);

    //println!("{:?}", (1 as u64).to_be_bytes());

    //let testcase0 = "test".to_string();

    let x: [u8; 4] = [0b1011_0000u8, 0b1011_0000u8, 0b1011_0000u8, 0b1011_0000u8];
    let y: [u8; 4] = [0b0011_1010u8, 0b0011_1010u8, 0b0011_1010u8, 0b0011_1010u8];
    let z: [u8; 4] = [0b1101_1100u8, 0b1101_1100u8, 0b1101_1100u8, 0b1101_1100u8];
    dbg!(function_I(&x, &y, &z));

    dbg!(bits_plus(&x, &y));
}
<commit_msg>trick<commit_after>#![feature(exclusive_range_pattern)]
fn read_string(a: String) -> Vec<u8> {
    a.bytes().collect()
}

fn fill_data(a: &mut Vec<u8>) {
    //fill data to N * 512 bits + 448 bits
    //N * 64 * 8 + 56 * 8
    let length = a.len();
    let module = length % 64;
    match module {
        0..56 => {
            a.push(0b1000_0000u8);
            let left_bytes = 55 - module;
            for _ in 0..left_bytes {
                a.push(0b0000_0000u8)
            }
        }
        57..=63 => {
            a.push(0b1000_0000u8);
            let left_bytes = 63 - module + 56;
            for _ in 0..left_bytes {
                a.push(0b0000_0000u8)
            }
        }
        _ => {}
    }

    //then give length of this data
    let mut add_length = ((length * 8) as u64).to_be_bytes().to_vec();
    a.append(&mut add_length);
}

fn function_F(x: &[u8; 4], y: &[u8; 4], z: &[u8; 4]) -> [u8; 4] {
    let mut temp0: [u8; 4] = [0, 0, 0, 0];
    for i in 0..4 {
        temp0[i] = x[i] & y[i];
    }
    dbg!(temp0);
    dbg!(x);

    let mut temp1: [u8; 4] = [0, 0, 0, 0];
    for i in 0..4 {
        temp1[i] = !x[i] & z[i];
    }
    dbg!(temp1);

    let mut temp2: [u8; 4] = [0, 0, 0, 0];
    for i in 0..4 {
        temp2[i] = temp0[i] | temp1[i];
    }

    temp2
}

fn function_G(x: &[u8; 4], y: &[u8; 4], z: &[u8; 4]) -> [u8; 4] {
    let mut temp0: [u8; 4] = [0, 0, 0, 0];
    for i in 0..4 {
        temp0[i] = x[i] & z[i];
    }
    dbg!(temp0);
    dbg!(x);

    let mut temp1: [u8; 4] = [0, 0, 0, 0];
    for i in 0..4 {
        temp1[i] = y[i] & !z[i];
    }
    dbg!(temp1);

    let mut temp2: [u8; 4] = [0, 0, 0, 0];
    for i in 0..4 {
        temp2[i] = temp0[i] | temp1[i];
    }

    temp2
}

fn function_H(x: &[u8; 4], y: &[u8; 4], z: &[u8; 4]) -> [u8; 4] {
    let mut temp0: [u8; 4] = [0, 0, 0, 0];
    for i in 0..4 {
        temp0[i] = x[i] ^ y[i] ^ z[i];
    }
    temp0
}

fn function_I(x: &[u8; 4], y: &[u8; 4], z: &[u8; 4]) -> [u8; 4] {
    let mut temp0: [u8; 4] = [0, 0, 0, 0];
    for i in 0..4 {
        temp0[i] = y[i] ^ (x[i] | !z[i]);
    }
    temp0
}

fn bits_plus(x: &[u8; 4], y: &[u8; 4]) -> [u8; 4] {
    let mut result: [u8; 4] = [0, 0, 0, 0];

    for i in 0..4 {
        result[i] = x[i] + y[i]
    }

    result
}

//:= TODO
//fn offset_func()

//:= todo
fn wrap_function<FF>(
    a: &mut [u8; 4],
    b: &[u8; 4],
    c: &[u8; 4],
    d: &[u8; 4],
    mj: &[u8; 4],
    s: &[u8; 4],
    ti: &[u8; 4],
    fu: FF,
) where
    FF: Fn(&[u8; 4], &[u8; 4], &[u8; 4]) -> [u8; 4],
{
}

fn main() {
    // let mut a = vec![0b1011_0000u8];
    // fill_data(&mut a);
    // println!("{:?}", a.len());

    let mut b = [0b1001_1001u8; 67].to_vec();
    fill_data(&mut b);
    println!("{:?}", b);

    //println!("{:?}", (1 as u64).to_be_bytes());

    //let testcase0 = "test".to_string();

    let x: [u8; 4] = [0b1011_0000u8, 0b1011_0000u8, 0b1011_0000u8, 0b1011_0000u8];
    let y: [u8; 4] = [0b0011_1010u8, 0b0011_1010u8, 0b0011_1010u8, 0b0011_1010u8];
    let z: [u8; 4] = [0b1101_1100u8, 0b1101_1100u8, 0b1101_1100u8, 0b1101_1100u8];
    dbg!(function_I(&x, &y, &z));

    dbg!(bits_plus(&x, &y));
}
<|endoftext|>


<commit_before>pub fn pathmatch(pattern: &str, pathstring: &str) -> bool
{
    use std::str::Chars;
    let pattern_chars = pattern.chars();
    let path_chars = pathstring.chars();
    return pathmatch_impl(pattern_chars, path_chars);

    fn pathmatch_impl(mut pattern_chars: Chars, mut path_chars: Chars) -> bool
    {
        loop {
            match pattern_chars.next() {
                None => return path_chars.next().is_none(),
                Some('?') => match path_chars.next() {
                    None | Some('/') => return false,
                    _ => (),
                },
                Some('*') => match pattern_chars.peekable().peek() {
                    Some(&'*') => {
                        pattern_chars.next();
                        loop {
                            if pathmatch_impl(pattern_chars, path_chars) {
                                return true;
                            }
                            match path_chars.next() {
                                None => break,
                                _ => (),
                            }
                        }
                    },
                    _ => loop {
                        if pathmatch_impl(pattern_chars, path_chars) {
                            return true;
                        }
                        match path_chars.next() {
                            None => break,
                            Some('/') => return false,
                            _ => (),
                        }
                    },
                },
                Some(pc) => {
                    match path_chars.next() {
                        None => return false,
                        Some(sc) => if pc != sc { return false },
                    }
                }
            }
        }
    }
}

#[test]
fn pathmatch_test_anypath()
{
    assert!(pathmatch("**", ""));
    assert!(pathmatch("a**", "a/b/c"));
    assert!(pathmatch("**c", "a/b/c"));
    assert!(pathmatch("a**c", "a/b/c"));
    assert!(pathmatch("a/**/c", "a/b/c"));
    assert!(pathmatch("**/c", "a/b/c"));
    assert!(!pathmatch("**/g", "e/fg"));
    assert!(pathmatch("a/**", "a/b/c"));
    assert!(!pathmatch("e/**", "ef/g"));
}

#[test]
fn pathmatch_test_pathsep()
{
    assert!(!pathmatch("a?b", "a/b"));
    assert!(!pathmatch("a*b", "a/b"));
    assert!(pathmatch("a*/b", "a/b"));
    assert!(pathmatch("a/*b", "a/b"));
}

#[test]
fn pathmatch_test_anyname()
{
    assert!(pathmatch("*", ""));
    assert!(pathmatch("*", "?"));
    assert!(pathmatch("*", "d?F"));
    assert!(pathmatch("a*", "a"));
    assert!(pathmatch("a*", "abcdef"));
    assert!(pathmatch("*f", "abcdef"));
    assert!(pathmatch("a*f", "abcdef"));
    assert!(pathmatch("*cd*", "abcdef"));
    assert!(pathmatch("a*cd*f", "abcdef"));
    assert!(pathmatch("acdf", "acdf"));
    assert!(!pathmatch("*a", "abc"));
    assert!(!pathmatch("c*", "abc"));
}

#[test]
fn pathmatch_test_anychar()
{
    assert!(pathmatch("?", "?"));
    assert!(pathmatch("?", "a"));
    assert!(pathmatch("??", "BC"));
    assert!(pathmatch("B?", "BC"));
    assert!(pathmatch("?C", "BC"));
    assert!(!pathmatch("?", ""));
    assert!(!pathmatch("D?", "BC"));
    assert!(!pathmatch("?E", "BC"));
}

#[test]
fn pathmatch_test_exact_equality()
{
    assert!(pathmatch("", ""));
    assert!(pathmatch("a", "a"));
    assert!(pathmatch("BC", "BC"));
    assert!(!pathmatch("a", ""));
    assert!(!pathmatch("", "BC"));
}

<commit_msg>test for special cases of anypath<commit_after>pub fn pathmatch(pattern: &str, pathstring: &str) -> bool
{
    use std::str::Chars;
    let pattern_chars = pattern.chars();
    let path_chars = pathstring.chars();
    return pathmatch_impl(pattern_chars, path_chars);

    fn pathmatch_impl(mut pattern_chars: Chars, mut path_chars: Chars) -> bool
    {
        if char_iter_equals(pattern_chars, "**/", true) {
            // How can I convert Skip<Chars> to Chars to make use of .skip(3) ?
            let mut pattern_chars_copy = pattern_chars;
            pattern_chars_copy.next();
            pattern_chars_copy.next();
            pattern_chars_copy.next();
            if pathmatch_impl(pattern_chars_copy, path_chars) {
                return true;
            }
        }
        loop {
            if char_iter_equals(pattern_chars, "/**", false) && path_chars.peekable().is_empty() {
                return true;
            }
            match pattern_chars.next() {
                None => return path_chars.next().is_none(),
                Some('?') => match path_chars.next() {
                    None | Some('/') => return false,
                    _ => (),
                },
                Some('*') => match pattern_chars.peekable().peek() {
                    Some(&'*') => {
                        pattern_chars.next();
                        loop {
                            if pathmatch_impl(pattern_chars, path_chars) {
                                return true;
                            }
                            match path_chars.next() {
                                None => break,
                                _ => (),
                            }
                        }
                    },
                    _ => loop {
                        if pathmatch_impl(pattern_chars, path_chars) {
                            return true;
                        }
                        match path_chars.next() {
                            None => break,
                            Some('/') => return false,
                            _ => (),
                        }
                    },
                },
                Some('/') => {
                    match path_chars.next() {
                        None => return false,
                        Some(sc) => if sc != '/' { return false },
                    }
                    if char_iter_equals(pattern_chars, "**/", true) && pathmatch_impl(pattern_chars, path_chars) {
                        return true;
                    }
                },
                Some(pc) => {
                    match path_chars.next() {
                        None => return false,
                        Some(sc) => if pc != sc { return false },
                    }
                }
            }
        }
    }

    fn char_iter_equals(mut char_iter: Chars, needle: &str, startswith: bool) -> bool
    {
        for needle_char in needle.chars() {
            match char_iter.next() {
                Some(x) if x == needle_char => (),
                _ => return false,
            }
        }
        return if startswith { true } else { char_iter.next().is_none() };
    }
}

#[test]
fn pathmatch_test_collapse()
{
    // `/**/` may match a single slash.
    assert!(pathmatch("foo/**/bar", "foo/bar"));
    assert!(pathmatch("foo/**/bar", "foo//bar"));
    assert!(pathmatch("foo/*/**/bar", "foo//bar"));
    assert!(!pathmatch("foo/*/**/bar", "foo/bar"));
    assert!(!pathmatch("foo/**/*/bar", "foo/bar"));
    assert!(pathmatch("foo/**/", "foo/"));
    assert!(!pathmatch("foo/**/", "foo"));
    assert!(pathmatch("/**/bar", "/bar"));
    assert!(!pathmatch("/**/bar", "bar"));
}

#[test]
fn pathmatch_test_anypath_leading()
{
    // `**/` at the beginning of pattern may match nothing or a slash.
    assert!(pathmatch("**/bar", "bar"));
    assert!(pathmatch("**/bar", "/bar"));
    assert!(!pathmatch("a**/bar", "abar"));
    assert!(!pathmatch("*/bar", "bar")); // does not apply to `*/`
}

#[test]
fn pathmatch_test_anypath_trailing()
{
    // `/**` at the end of pattern may match nothing or a slash.
    assert!(pathmatch("foo/**", "foo"));
    assert!(pathmatch("foo/**", "foo/"));
    assert!(!pathmatch("foo/**g", "foog"));
    assert!(!pathmatch("foo/*", "foo")); // does not apply to `/*`
}

#[test]
fn pathmatch_test_anypath()
{
    assert!(pathmatch("**", ""));
    assert!(pathmatch("a**", "a/b/c"));
    assert!(pathmatch("**c", "a/b/c"));
    assert!(pathmatch("a**c", "a/b/c"));
    assert!(pathmatch("a/**/c", "a/b/c"));
    assert!(pathmatch("**/c", "a/b/c"));
    assert!(!pathmatch("**/g", "e/fg"));
    assert!(pathmatch("a/**", "a/b/c"));
    assert!(!pathmatch("e/**", "ef/g"));
}

#[test]
fn pathmatch_test_pathsep()
{
    assert!(!pathmatch("a?b", "a/b"));
    assert!(!pathmatch("a*b", "a/b"));
    assert!(pathmatch("a*/b", "a/b"));
    assert!(pathmatch("a/*b", "a/b"));
}

#[test]
fn pathmatch_test_anyname()
{
    assert!(pathmatch("*", ""));
    assert!(pathmatch("*", "?"));
    assert!(pathmatch("*", "d?F"));
    assert!(pathmatch("a*", "a"));
    assert!(pathmatch("a*", "abcdef"));
    assert!(pathmatch("*f", "abcdef"));
    assert!(pathmatch("a*f", "abcdef"));
    assert!(pathmatch("*cd*", "abcdef"));
    assert!(pathmatch("a*cd*f", "abcdef"));
    assert!(pathmatch("acdf", "acdf"));
    assert!(!pathmatch("*a", "abc"));
    assert!(!pathmatch("c*", "abc"));
}

#[test]
fn pathmatch_test_anychar()
{
    assert!(pathmatch("?", "?"));
    assert!(pathmatch("?", "a"));
    assert!(pathmatch("??", "BC"));
    assert!(pathmatch("B?", "BC"));
    assert!(pathmatch("?C", "BC"));
    assert!(!pathmatch("?", ""));
    assert!(!pathmatch("D?", "BC"));
    assert!(!pathmatch("?E", "BC"));
}

#[test]
fn pathmatch_test_exact_equality()
{
    assert!(pathmatch("", ""));
    assert!(pathmatch("a", "a"));
    assert!(pathmatch("BC", "BC"));
    assert!(!pathmatch("a", ""));
    assert!(!pathmatch("", "BC"));
}

<|endoftext|>


<commit_before>mod err;

use anime::local::detect;
use anime::local::{EpisodeParser, Episodes};
use anime::remote::anilist::AniList;
use anime::remote::{RemoteService, SeriesInfo};
use err::{Error, Result};
use gumdrop::Options;
use snafu::{ensure, OptionExt, ResultExt};
use std::borrow::Cow;
use std::collections::HashMap;
use std::fs;
use std::io;
use std::os::unix::fs::symlink;
use std::path::{Path, PathBuf};
use std::thread;
use std::time::Duration;

const PARSER_TITLE_REP: &str = "{title}";
const PARSER_EPISODE_REP: &str = "{episode}";

#[derive(Options)]
struct CmdOptions {
    #[options(help = "print help message")]
    help: bool,
    #[options(free, required, help = "the path pointing to the series to split")]
    path: PathBuf,
    #[options(
        help = "the path to create the split seasons in. By default, the parent directory of the series path will be used"
    )]
    out_dir: Option<PathBuf>,
    #[options(
        help = "the anime series ID. Use if the program doesn't detect the right series automatically"
    )]
    series_id: Option<u32>,
    #[options(
        help = "the format to rename the files as. Must contain \"{title}\" and \"{episode}\""
    )]
    name_format: Option<String>,
    #[options(help = "the custom regex pattern to match episode files with")]
    matcher: Option<String>,
    #[options(no_short, help = "link episode files via symlinks")]
    symlink: bool,
    #[options(no_short, help = "link episode files via hardlinks")]
    hardlink: bool,
    #[options(no_short, help = "link episode files via file moves")]
    move_files: bool,
}

fn main() {
    let args = CmdOptions::parse_args_default_or_exit();

    if let Err(err) = run(args) {
        err::display_error(err);
        std::process::exit(1);
    }
}

fn run(args: CmdOptions) -> Result<()> {
    let remote = AniList::unauthenticated();

    let path = args.path.canonicalize().context(err::IO)?;

    let name_format = match &args.name_format {
        Some(format) => NameFormat::new(format)?,
        None => NameFormat::new(format!("{} - {}.mkv", PARSER_TITLE_REP, PARSER_EPISODE_REP))?,
    };

    let matcher = match &args.matcher {
        Some(pattern) => {
            EpisodeParser::custom_with_replacements(pattern, PARSER_TITLE_REP, PARSER_EPISODE_REP)?
        }
        None => EpisodeParser::default(),
    };

    let out_dir = match &args.out_dir {
        Some(out_dir) => PathBuf::from(out_dir),
        None => path.parent().context(err::NoDirParent)?.into(),
    };

    let all_episodes = Episodes::parse_all(&path, &matcher)?;

    match all_episodes.len() {
        len if len > 1 => {
            println!("found multiple titles in directory.. these will be moved instead\nrerun the tool afterwards to split up merged seasons\n");

            let data = SeriesData {
                name_format,
                link_method: LinkMethod::Move,
                path,
                out_dir,
            };

            split_multiple_titles(&args, data, all_episodes, remote)
        }
        1 => {
            let (_, episodes) = all_episodes.into_iter().next().unwrap();

            let series = {
                let title = parse_path_title(&path)?;
                find_series_info(&args, title, &remote)?
            };

            println!("processing merged seasons of {}\n", series.title.preferred);

            let data = SeriesData {
                name_format,
                link_method: LinkMethod::from_args(&args),
                path,
                out_dir,
            };

            format_sequels(data, series, episodes, remote)
        }
        _ => Ok(()),
    }
}

fn split_multiple_titles(
    args: &CmdOptions,
    data: SeriesData,
    all_episodes: HashMap<String, Episodes>,
    remote: AniList,
) -> Result<()> {
    let original_title = parse_path_title(&data.path)?;

    for (title, episodes) in all_episodes {
        if title == original_title {
            continue;
        }

        println!("moving {}", title);

        let info = find_series_info(args, title, &remote)?;
        let actions = PendingActions::generate(&data, &info, &episodes, 0)?;

        if !actions.confirm_proceed()? {
            continue;
        }

        actions.execute()?;
    }

    Ok(())
}

fn format_sequels(
    data: SeriesData,
    mut info: SeriesInfo,
    episodes: Episodes,
    remote: AniList,
) -> Result<()> {
    let mut episode_offset = 0;
    let mut total_actions = 0;

    while let Some(sequel) = info.sequel {
        info = remote.search_info_by_id(sequel)?;
        episode_offset += info.episodes;

        println!("looking for {}", info.title.preferred);

        let actions = match PendingActions::generate(&data, &info, &episodes, episode_offset) {
            Ok(actions) => actions,
            Err(err @ Error::NoEpisodes) => {
                println!("| {}", err);
                return Ok(());
            }
            Err(err) => return Err(err),
        };

        if !actions.confirm_proceed()? {
            continue;
        }

        total_actions += actions.execute()?;

        // We don't need to sleep if there isn't another sequel
        if info.sequel.is_none() {
            break;
        }

        thread::sleep(Duration::from_millis(500));
    }

    println!("\n{} actions performed", total_actions);
    Ok(())
}

struct SeriesData {
    name_format: NameFormat,
    link_method: LinkMethod,
    path: PathBuf,
    out_dir: PathBuf,
}

struct NameFormat(String);

impl NameFormat {
    fn new<S>(format: S) -> Result<NameFormat>
    where
        S: Into<String>,
    {
        let format = format.into();

        ensure!(
            format.contains(PARSER_TITLE_REP),
            err::MissingFormatGroup { group: "title" }
        );

        ensure!(
            format.contains(PARSER_EPISODE_REP),
            err::MissingFormatGroup { group: "episode" }
        );

        Ok(NameFormat(format))
    }

    fn process<S>(&self, name: S, episode: u32) -> String
    where
        S: AsRef<str>,
    {
        self.0
            .replace(PARSER_TITLE_REP, name.as_ref())
            .replace(PARSER_EPISODE_REP, &format!("{:02}", episode))
    }
}

#[derive(Copy, Clone)]
enum LinkMethod {
    Symlink,
    Hardlink,
    Move,
}

impl LinkMethod {
    fn from_args(args: &CmdOptions) -> Self {
        if args.symlink {
            Self::Symlink
        } else if args.hardlink {
            Self::Hardlink
        } else if args.move_files {
            Self::Move
        } else {
            Self::default()
        }
    }

    fn execute<P>(self, from: P, to: P) -> Result<()>
    where
        P: AsRef<Path>,
    {
        let from = from.as_ref();
        let to = to.as_ref();

        let result = match self {
            Self::Symlink => symlink(from, to),
            Self::Hardlink => fs::hard_link(from, to),
            Self::Move => fs::rename(from, to),
        };

        result.context(err::LinkIO { from, to })
    }

    fn plural_str(self) -> &'static str {
        match self {
            Self::Symlink => "symlinks",
            Self::Hardlink => "hardlinks",
            Self::Move => "moves",
        }
    }
}

impl Default for LinkMethod {
    fn default() -> LinkMethod {
        LinkMethod::Symlink
    }
}

struct FormatAction {
    from: PathBuf,
    to: PathBuf,
}

impl FormatAction {
    #[inline(always)]
    fn new<S, O>(from: S, to: O) -> Self
    where
        S: Into<PathBuf>,
        O: Into<PathBuf>,
    {
        Self {
            from: from.into(),
            to: to.into(),
        }
    }
}

struct PendingActions {
    actions: Vec<FormatAction>,
    out_dir: PathBuf,
    method: LinkMethod,
}

impl PendingActions {
    fn generate(
        data: &SeriesData,
        info: &SeriesInfo,
        episodes: &Episodes,
        episode_offset: u32,
    ) -> Result<Self> {
        let out_dir = data.out_dir.join(&info.title.preferred);
        let mut actions = Vec::new();
        let mut has_any_episodes = false;

        for real_ep_num in (1 + episode_offset)..=(episode_offset + info.episodes) {
            let episode = match episodes.get(real_ep_num) {
                Some(episode) => {
                    has_any_episodes = true;
                    episode
                }
                None => continue,
            };

            let episode_path = data.path.join(&episode.filename);

            let new_filename = data
                .name_format
                .process(&info.title.preferred, real_ep_num - episode_offset);

            let new_path = out_dir.join(new_filename);

            actions.push(FormatAction::new(episode_path, new_path));
        }

        if !has_any_episodes {
            return Err(Error::NoEpisodes);
        }

        Ok(Self {
            actions,
            out_dir,
            method: data.link_method,
        })
    }

    fn confirm_proceed(&self) -> Result<bool> {
        if self.actions.is_empty() {
            println!("| no actions to be performed");
            return Ok(true);
        }

        println!(
            "| the following file {} will be made:",
            self.method.plural_str()
        );

        for action in &self.actions {
            println!(
                "{} -> {}",
                action.from.to_string_lossy(),
                action.to.to_string_lossy()
            );
        }

        println!("| is this okay? (Y/n)");

        let answer = {
            let mut buffer = String::new();
            io::stdin().read_line(&mut buffer).context(err::IO)?;
            buffer.trim_end().to_string()
        };

        match answer.as_ref() {
            "n" | "N" => Ok(false),
            _ => Ok(true),
        }
    }

    fn execute(self) -> Result<u32> {
        if self.actions.is_empty() {
            return Ok(0);
        }

        if !self.out_dir.exists() {
            fs::create_dir_all(&self.out_dir).context(err::FileIO {
                path: &self.out_dir,
            })?;
        }

        let mut actions_performed = 0;

        for action in self.actions {
            match self.method.execute(action.from, action.to) {
                Ok(_) => actions_performed += 1,
                Err(Error::LinkIO { source, .. })
                    if source.kind() == io::ErrorKind::AlreadyExists =>
                {
                    actions_performed += 1;
                }
                Err(err) => eprintln!("{}", err),
            }
        }

        Ok(actions_performed)
    }
}

fn parse_path_title<P>(path: P) -> Result<String>
where
    P: AsRef<Path>,
{
    let path = path.as_ref();
    ensure!(path.is_dir(), err::NotADirectory);

    let title = detect::dir::parse_title(path).context(err::FolderTitleParse)?;
    Ok(title)
}

fn find_series_info<S>(args: &CmdOptions, title: S, remote: &AniList) -> Result<SeriesInfo>
where
    S: AsRef<str>,
{
    const MIN_CONFIDENCE: f32 = 0.85;

    match args.series_id {
        Some(id) => {
            let info = remote.search_info_by_id(id)?;
            Ok(info)
        }
        None => {
            let title = title.as_ref();
            let results = remote
                .search_info_by_name(title)?
                .into_iter()
                .map(Cow::Owned);

            SeriesInfo::closest_match(title, MIN_CONFIDENCE, results)
                .map(|(_, info)| info.into_owned())
                .context(err::UnableToDetectSeries { title })
        }
    }
}

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

    #[test]
    #[should_panic]
    fn name_format_detect_no_group() {
        NameFormat::new("useless").unwrap();
    }

    #[test]
    fn name_format_detect_no_title_group() {
        let result = NameFormat::new(format!("missing_title - {}.mkv", PARSER_EPISODE_REP));

        match result {
            Err(err::Error::MissingFormatGroup { group }) if group == "title" => (),
            Ok(_) => panic!("expected missing title group error"),
            Err(err) => panic!("expected missing title group error, got: {:?}", err),
        }
    }

    #[test]
    fn name_format_detect_no_episode_group() {
        let result = NameFormat::new(format!("{} - missing_episode.mkv", PARSER_TITLE_REP));

        match result {
            Err(err::Error::MissingFormatGroup { group }) if group == "episode" => (),
            Ok(_) => panic!("expected missing episode group error"),
            Err(err) => panic!("expected missing episode group error, got: {:?}", err),
        }
    }
}
<commit_msg>anisplit: Update for last anime lib change<commit_after>mod err;

use anime::local::detect;
use anime::local::{EpisodeParser, Episodes};
use anime::remote::anilist::AniList;
use anime::remote::{RemoteService, SeriesInfo};
use err::{Error, Result};
use gumdrop::Options;
use snafu::{ensure, OptionExt, ResultExt};
use std::borrow::Cow;
use std::collections::HashMap;
use std::fs;
use std::io;
use std::os::unix::fs::symlink;
use std::path::{Path, PathBuf};
use std::thread;
use std::time::Duration;

const PARSER_TITLE_REP: &str = "{title}";
const PARSER_EPISODE_REP: &str = "{episode}";

#[derive(Options)]
struct CmdOptions {
    #[options(help = "print help message")]
    help: bool,
    #[options(free, required, help = "the path pointing to the series to split")]
    path: PathBuf,
    #[options(
        help = "the path to create the split seasons in. By default, the parent directory of the series path will be used"
    )]
    out_dir: Option<PathBuf>,
    #[options(
        help = "the anime series ID. Use if the program doesn't detect the right series automatically"
    )]
    series_id: Option<u32>,
    #[options(
        help = "the format to rename the files as. Must contain \"{title}\" and \"{episode}\""
    )]
    name_format: Option<String>,
    #[options(help = "the custom regex pattern to match episode files with")]
    matcher: Option<String>,
    #[options(no_short, help = "link episode files via symlinks")]
    symlink: bool,
    #[options(no_short, help = "link episode files via hardlinks")]
    hardlink: bool,
    #[options(no_short, help = "link episode files via file moves")]
    move_files: bool,
}

fn main() {
    let args = CmdOptions::parse_args_default_or_exit();

    if let Err(err) = run(args) {
        err::display_error(err);
        std::process::exit(1);
    }
}

fn run(args: CmdOptions) -> Result<()> {
    let remote = AniList::Unauthenticated;
    let path = args.path.canonicalize().context(err::IO)?;

    let name_format = match &args.name_format {
        Some(format) => NameFormat::new(format)?,
        None => NameFormat::new(format!("{} - {}.mkv", PARSER_TITLE_REP, PARSER_EPISODE_REP))?,
    };

    let matcher = match &args.matcher {
        Some(pattern) => {
            EpisodeParser::custom_with_replacements(pattern, PARSER_TITLE_REP, PARSER_EPISODE_REP)?
        }
        None => EpisodeParser::default(),
    };

    let out_dir = match &args.out_dir {
        Some(out_dir) => PathBuf::from(out_dir),
        None => path.parent().context(err::NoDirParent)?.into(),
    };

    let all_episodes = Episodes::parse_all(&path, &matcher)?;

    match all_episodes.len() {
        len if len > 1 => {
            println!("found multiple titles in directory.. these will be moved instead\nrerun the tool afterwards to split up merged seasons\n");

            let data = SeriesData {
                name_format,
                link_method: LinkMethod::Move,
                path,
                out_dir,
            };

            split_multiple_titles(&args, data, all_episodes, remote)
        }
        1 => {
            let (_, episodes) = all_episodes.into_iter().next().unwrap();

            let series = {
                let title = parse_path_title(&path)?;
                find_series_info(&args, title, &remote)?
            };

            println!("processing merged seasons of {}\n", series.title.preferred);

            let data = SeriesData {
                name_format,
                link_method: LinkMethod::from_args(&args),
                path,
                out_dir,
            };

            format_sequels(data, series, episodes, remote)
        }
        _ => Ok(()),
    }
}

fn split_multiple_titles(
    args: &CmdOptions,
    data: SeriesData,
    all_episodes: HashMap<String, Episodes>,
    remote: AniList,
) -> Result<()> {
    let original_title = parse_path_title(&data.path)?;

    for (title, episodes) in all_episodes {
        if title == original_title {
            continue;
        }

        println!("moving {}", title);

        let info = find_series_info(args, title, &remote)?;
        let actions = PendingActions::generate(&data, &info, &episodes, 0)?;

        if !actions.confirm_proceed()? {
            continue;
        }

        actions.execute()?;
    }

    Ok(())
}

fn format_sequels(
    data: SeriesData,
    mut info: SeriesInfo,
    episodes: Episodes,
    remote: AniList,
) -> Result<()> {
    let mut episode_offset = 0;
    let mut total_actions = 0;

    while let Some(sequel) = info.sequel {
        info = remote.search_info_by_id(sequel)?;
        episode_offset += info.episodes;

        println!("looking for {}", info.title.preferred);

        let actions = match PendingActions::generate(&data, &info, &episodes, episode_offset) {
            Ok(actions) => actions,
            Err(err @ Error::NoEpisodes) => {
                println!("| {}", err);
                return Ok(());
            }
            Err(err) => return Err(err),
        };

        if !actions.confirm_proceed()? {
            continue;
        }

        total_actions += actions.execute()?;

        // We don't need to sleep if there isn't another sequel
        if info.sequel.is_none() {
            break;
        }

        thread::sleep(Duration::from_millis(500));
    }

    println!("\n{} actions performed", total_actions);
    Ok(())
}

struct SeriesData {
    name_format: NameFormat,
    link_method: LinkMethod,
    path: PathBuf,
    out_dir: PathBuf,
}

struct NameFormat(String);

impl NameFormat {
    fn new<S>(format: S) -> Result<NameFormat>
    where
        S: Into<String>,
    {
        let format = format.into();

        ensure!(
            format.contains(PARSER_TITLE_REP),
            err::MissingFormatGroup { group: "title" }
        );

        ensure!(
            format.contains(PARSER_EPISODE_REP),
            err::MissingFormatGroup { group: "episode" }
        );

        Ok(NameFormat(format))
    }

    fn process<S>(&self, name: S, episode: u32) -> String
    where
        S: AsRef<str>,
    {
        self.0
            .replace(PARSER_TITLE_REP, name.as_ref())
            .replace(PARSER_EPISODE_REP, &format!("{:02}", episode))
    }
}

#[derive(Copy, Clone)]
enum LinkMethod {
    Symlink,
    Hardlink,
    Move,
}

impl LinkMethod {
    fn from_args(args: &CmdOptions) -> Self {
        if args.symlink {
            Self::Symlink
        } else if args.hardlink {
            Self::Hardlink
        } else if args.move_files {
            Self::Move
        } else {
            Self::default()
        }
    }

    fn execute<P>(self, from: P, to: P) -> Result<()>
    where
        P: AsRef<Path>,
    {
        let from = from.as_ref();
        let to = to.as_ref();

        let result = match self {
            Self::Symlink => symlink(from, to),
            Self::Hardlink => fs::hard_link(from, to),
            Self::Move => fs::rename(from, to),
        };

        result.context(err::LinkIO { from, to })
    }

    fn plural_str(self) -> &'static str {
        match self {
            Self::Symlink => "symlinks",
            Self::Hardlink => "hardlinks",
            Self::Move => "moves",
        }
    }
}

impl Default for LinkMethod {
    fn default() -> LinkMethod {
        LinkMethod::Symlink
    }
}

struct FormatAction {
    from: PathBuf,
    to: PathBuf,
}

impl FormatAction {
    #[inline(always)]
    fn new<S, O>(from: S, to: O) -> Self
    where
        S: Into<PathBuf>,
        O: Into<PathBuf>,
    {
        Self {
            from: from.into(),
            to: to.into(),
        }
    }
}

struct PendingActions {
    actions: Vec<FormatAction>,
    out_dir: PathBuf,
    method: LinkMethod,
}

impl PendingActions {
    fn generate(
        data: &SeriesData,
        info: &SeriesInfo,
        episodes: &Episodes,
        episode_offset: u32,
    ) -> Result<Self> {
        let out_dir = data.out_dir.join(&info.title.preferred);
        let mut actions = Vec::new();
        let mut has_any_episodes = false;

        for real_ep_num in (1 + episode_offset)..=(episode_offset + info.episodes) {
            let episode = match episodes.get(real_ep_num) {
                Some(episode) => {
                    has_any_episodes = true;
                    episode
                }
                None => continue,
            };

            let episode_path = data.path.join(&episode.filename);

            let new_filename = data
                .name_format
                .process(&info.title.preferred, real_ep_num - episode_offset);

            let new_path = out_dir.join(new_filename);

            actions.push(FormatAction::new(episode_path, new_path));
        }

        if !has_any_episodes {
            return Err(Error::NoEpisodes);
        }

        Ok(Self {
            actions,
            out_dir,
            method: data.link_method,
        })
    }

    fn confirm_proceed(&self) -> Result<bool> {
        if self.actions.is_empty() {
            println!("| no actions to be performed");
            return Ok(true);
        }

        println!(
            "| the following file {} will be made:",
            self.method.plural_str()
        );

        for action in &self.actions {
            println!(
                "{} -> {}",
                action.from.to_string_lossy(),
                action.to.to_string_lossy()
            );
        }

        println!("| is this okay? (Y/n)");

        let answer = {
            let mut buffer = String::new();
            io::stdin().read_line(&mut buffer).context(err::IO)?;
            buffer.trim_end().to_string()
        };

        match answer.as_ref() {
            "n" | "N" => Ok(false),
            _ => Ok(true),
        }
    }

    fn execute(self) -> Result<u32> {
        if self.actions.is_empty() {
            return Ok(0);
        }

        if !self.out_dir.exists() {
            fs::create_dir_all(&self.out_dir).context(err::FileIO {
                path: &self.out_dir,
            })?;
        }

        let mut actions_performed = 0;

        for action in self.actions {
            match self.method.execute(action.from, action.to) {
                Ok(_) => actions_performed += 1,
                Err(Error::LinkIO { source, .. })
                    if source.kind() == io::ErrorKind::AlreadyExists =>
                {
                    actions_performed += 1;
                }
                Err(err) => eprintln!("{}", err),
            }
        }

        Ok(actions_performed)
    }
}

fn parse_path_title<P>(path: P) -> Result<String>
where
    P: AsRef<Path>,
{
    let path = path.as_ref();
    ensure!(path.is_dir(), err::NotADirectory);

    let title = detect::dir::parse_title(path).context(err::FolderTitleParse)?;
    Ok(title)
}

fn find_series_info<S>(args: &CmdOptions, title: S, remote: &AniList) -> Result<SeriesInfo>
where
    S: AsRef<str>,
{
    const MIN_CONFIDENCE: f32 = 0.85;

    match args.series_id {
        Some(id) => {
            let info = remote.search_info_by_id(id)?;
            Ok(info)
        }
        None => {
            let title = title.as_ref();
            let results = remote
                .search_info_by_name(title)?
                .into_iter()
                .map(Cow::Owned);

            SeriesInfo::closest_match(title, MIN_CONFIDENCE, results)
                .map(|(_, info)| info.into_owned())
                .context(err::UnableToDetectSeries { title })
        }
    }
}

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

    #[test]
    #[should_panic]
    fn name_format_detect_no_group() {
        NameFormat::new("useless").unwrap();
    }

    #[test]
    fn name_format_detect_no_title_group() {
        let result = NameFormat::new(format!("missing_title - {}.mkv", PARSER_EPISODE_REP));

        match result {
            Err(err::Error::MissingFormatGroup { group }) if group == "title" => (),
            Ok(_) => panic!("expected missing title group error"),
            Err(err) => panic!("expected missing title group error, got: {:?}", err),
        }
    }

    #[test]
    fn name_format_detect_no_episode_group() {
        let result = NameFormat::new(format!("{} - missing_episode.mkv", PARSER_TITLE_REP));

        match result {
            Err(err::Error::MissingFormatGroup { group }) if group == "episode" => (),
            Ok(_) => panic!("expected missing episode group error"),
            Err(err) => panic!("expected missing episode group error, got: {:?}", err),
        }
    }
}
<|endoftext|>