<commit_before>use runtime::Runtime;
use storage::backend::{StorageBackendError, StorageBackend};

use module::Module;
use module::ModuleError;
use module::CommandResult;
use module::CommandEnv;

use module::bm::header::build_header;
use module::bm::header::get_tags_from_header;
use storage::json::parser::JsonHeaderParser;
use storage::parser::{Parser, FileHeaderParser};
use storage::file::File;
use ui::file::{FilePrinter, TablePrinter};
use std::vec::IntoIter;

use clap::ArgMatches;
use regex::Regex;

pub fn add_command(module: &Module, env: CommandEnv) -> CommandResult {
    use url::Url;

    let url = env.matches.value_of("url").unwrap();

    if let Err(e) = Url::parse(url) {
        info!("Not an URL: '{}'", url);
        info!("  this will turn into an hard error before 0.1.0");
        debug!("URL parsing error: {:?}", e);
    }

    let tags = get_tags(env.rt, env.matches);
    info!("Adding url '{}' with tags '{:?}'", url, tags);

    let header  = build_header(&String::from(url), &tags);
    let file    = File::new_with_header(module, header);
    let parser  = Parser::new(JsonHeaderParser::new(None));
    let putres  = env.bk.put_file(file, &parser);

    putres.map_err(|sberr| {
        let mut err = ModuleError::new("Storage Backend Error");
        err.caused_by = Some(Box::new(sberr));
        err
    })
}

pub fn list_command(module: &Module, env: CommandEnv) -> CommandResult {
    let printer = TablePrinter::new(env.rt.is_verbose(), env.rt.is_debugging());
    let files   = get_filtered_files_from_backend(module, &env);

    debug!("Printing files now");
    files.map(|f| printer.print_files(f));

    Ok(())
}

pub fn remove_command(module: &Module, env: CommandEnv) -> CommandResult {
    let checked : bool = run_removal_checking(&env);
    debug!("Checked mode: {}", checked);
    if let Some(id) = get_id(env.rt, env.matches) {
        debug!("Remove by id: {}", id);

        let parser = Parser::new(JsonHeaderParser::new(None));
        env.bk.get_file_by_id(module, &id.into(), &parser).map(|file| {
            debug!("Remove file  : {:?}", file);

            if let Err(e) = env.bk.remove_file(module, file, checked) {
                debug!("Remove failed");
                let mut err = ModuleError::new("Removing file failed");
                err.caused_by = Some(Box::new(e));
                Err(err)
            } else {
                info!("Remove worked");
                Ok(())
            }
        }).unwrap_or({
            info!("No files found");
            Ok(())
        })
    } else {
        debug!("Remove more than one file");

        get_filtered_files_from_backend(module, &env).and_then(|files| {
            let nfiles = files.len();
            info!("Removing {} Files", nfiles);

            let errs = files.map(|file| {
                    debug!("Remove file: {:?}", file);
                    env.bk.remove_file(module, file, checked)
                })
                .filter(|e| e.is_err())
                .map(|e| {
                    let err = e.err().unwrap();
                    warn!("Error occured in Filesystem operation: {}", err);
                    err
                })
                .collect::<Vec<StorageBackendError>>();

            let nerrs = errs.len();

            if nerrs != 0 {
                warn!("{} Errors occured while removing {} files", nerrs, nfiles);
                let moderr = ModuleError::new("File removal failed");

                // TODO : Collect StorageBackendErrors

                Err(moderr)
            } else {
                Ok(())
            }
        })
    }
}

/*
 *
 * Private helpers
 *
 */

fn get_filtered_files_from_backend<'a>(module: &'a Module,
                                       env: &CommandEnv)
    -> Result<IntoIter<File<'a>>, ModuleError>
{
    let parser = Parser::new(JsonHeaderParser::new(None));
    let tags = get_tags(env.rt, env.matches);
    debug!("Tags: {:?}", tags);
    env.bk.iter_files(module, &parser)
        .map(|files| {
            let f = files.filter(|file| {
                debug!("Backend returns file: {:?}", file);
                if tags.len() != 0 {
                    debug!("Checking tags of: {:?}", file.id());
                    get_tags_from_header(&file.header()).iter()
                        .any(|t| tags.contains(t))
                } else {
                    true
                }
            }).filter(|file| {
                debug!("Checking matches of: {:?}", file.id());
                get_matcher(env.rt, env.matches)
                    .and_then(|r| Some(file.matches_with(&r)))
                    .unwrap_or(true)
            }).collect::<Vec<File>>();
            f.into_iter()
        }).map_err(|e| {
            debug!("Error from Backend: {:?}", e);
            let mut merr = ModuleError::new("Could not filter files");
            merr.caused_by = Some(Box::new(e));
            merr
        })
}

fn get_tags<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Vec<String> {
    debug!("Fetching tags from commandline");
    sub.value_of("tags").and_then(|tags|
                                  Some(tags.split(",")
                                       .into_iter()
                                       .map(|s| s.to_string())
                                       .filter(|e|
                                            if e.contains(" ") {
                                                warn!("Tag contains spaces: '{}'", e);
                                                false
                                            } else {
                                                true
                                            }).collect()
                                      )
                                 ).or(Some(vec![])).unwrap()

}

fn get_matcher<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<Regex> {
    debug!("Fetching matcher from commandline");
    if let Some(s) = sub.value_of("match") {
        if let Ok(r) = Regex::new(s) {
            return Some(r)
        } else {
            error!("Regex error, continuing without regex");
        }
    }
    None

}

fn get_id<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<String> {
    debug!("Fetching id from commandline");
    sub.value_of("id").and_then(|s| Some(String::from(s)))
}

/*
 * Checks whether the commandline call was set to run the removal "checked",
 * so if another entry from the store refers to this ID, do not remove the file.
 */
fn run_removal_checking(env: &CommandEnv) -> bool {
    env.matches.is_present("check")
}
<commit_msg>Rework remove_command()<commit_after>use runtime::Runtime;
use storage::backend::{StorageBackendError, StorageBackend};

use module::Module;
use module::ModuleError;
use module::CommandResult;
use module::CommandEnv;

use module::bm::header::build_header;
use module::bm::header::get_tags_from_header;
use storage::json::parser::JsonHeaderParser;
use storage::parser::{Parser, FileHeaderParser};
use storage::file::File;
use ui::file::{FilePrinter, TablePrinter};
use std::vec::IntoIter;

use clap::ArgMatches;
use regex::Regex;

pub fn add_command(module: &Module, env: CommandEnv) -> CommandResult {
    use url::Url;

    let url = env.matches.value_of("url").unwrap();

    if let Err(e) = Url::parse(url) {
        info!("Not an URL: '{}'", url);
        info!("  this will turn into an hard error before 0.1.0");
        debug!("URL parsing error: {:?}", e);
    }

    let tags = get_tags(env.rt, env.matches);
    info!("Adding url '{}' with tags '{:?}'", url, tags);

    let header  = build_header(&String::from(url), &tags);
    let file    = File::new_with_header(module, header);
    let parser  = Parser::new(JsonHeaderParser::new(None));
    let putres  = env.bk.put_file(file, &parser);

    putres.map_err(|sberr| {
        let mut err = ModuleError::new("Storage Backend Error");
        err.caused_by = Some(Box::new(sberr));
        err
    })
}

pub fn list_command(module: &Module, env: CommandEnv) -> CommandResult {
    let printer = TablePrinter::new(env.rt.is_verbose(), env.rt.is_debugging());
    let files   = get_filtered_files_from_backend(module, &env);

    debug!("Printing files now");
    files.map(|f| printer.print_files(f));

    Ok(())
}

pub fn remove_command(module: &Module, env: CommandEnv) -> CommandResult {
    let checked : bool = run_removal_checking(&env);
    debug!("Checked mode: {}", checked);
    if let Some(id) = get_id(env.rt, env.matches) {
        debug!("Remove by id: {}", id);

        let parser = Parser::new(JsonHeaderParser::new(None));
        let file = env.bk
            .get_file_by_id(module, &id.into(), &parser)
            .unwrap_or({
                info!("No files found");
                return Ok(())
            });

        debug!("Remove file: {:?}", file);

        if let Err(e) = env.bk.remove_file(module, file, checked) {
            debug!("Remove failed");
            let mut err = ModuleError::new("Removing file failed");
            err.caused_by = Some(Box::new(e));
            Err(err)
        } else {
            info!("Remove worked");
            Ok(())
        }
    } else {
        debug!("Remove more than one file");

        get_filtered_files_from_backend(module, &env).and_then(|files| {
            let nfiles = files.len();
            info!("Removing {} Files", nfiles);

            let errs = files.map(|file| {
                    debug!("Remove file: {:?}", file);
                    env.bk.remove_file(module, file, checked)
                })
                .filter(|e| e.is_err())
                .map(|e| {
                    let err = e.err().unwrap();
                    warn!("Error occured in Filesystem operation: {}", err);
                    err
                })
                .collect::<Vec<StorageBackendError>>();

            let nerrs = errs.len();

            if nerrs != 0 {
                warn!("{} Errors occured while removing {} files", nerrs, nfiles);
                let moderr = ModuleError::new("File removal failed");

                // TODO : Collect StorageBackendErrors

                Err(moderr)
            } else {
                Ok(())
            }
        })
    }
}

/*
 *
 * Private helpers
 *
 */

fn get_filtered_files_from_backend<'a>(module: &'a Module,
                                       env: &CommandEnv)
    -> Result<IntoIter<File<'a>>, ModuleError>
{
    let parser = Parser::new(JsonHeaderParser::new(None));
    let tags = get_tags(env.rt, env.matches);
    debug!("Tags: {:?}", tags);
    env.bk.iter_files(module, &parser)
        .map(|files| {
            let f = files.filter(|file| {
                debug!("Backend returns file: {:?}", file);
                if tags.len() != 0 {
                    debug!("Checking tags of: {:?}", file.id());
                    get_tags_from_header(&file.header()).iter()
                        .any(|t| tags.contains(t))
                } else {
                    true
                }
            }).filter(|file| {
                debug!("Checking matches of: {:?}", file.id());
                get_matcher(env.rt, env.matches)
                    .and_then(|r| Some(file.matches_with(&r)))
                    .unwrap_or(true)
            }).collect::<Vec<File>>();
            f.into_iter()
        }).map_err(|e| {
            debug!("Error from Backend: {:?}", e);
            let mut merr = ModuleError::new("Could not filter files");
            merr.caused_by = Some(Box::new(e));
            merr
        })
}

fn get_tags<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Vec<String> {
    debug!("Fetching tags from commandline");
    sub.value_of("tags").and_then(|tags|
                                  Some(tags.split(",")
                                       .into_iter()
                                       .map(|s| s.to_string())
                                       .filter(|e|
                                            if e.contains(" ") {
                                                warn!("Tag contains spaces: '{}'", e);
                                                false
                                            } else {
                                                true
                                            }).collect()
                                      )
                                 ).or(Some(vec![])).unwrap()

}

fn get_matcher<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<Regex> {
    debug!("Fetching matcher from commandline");
    if let Some(s) = sub.value_of("match") {
        if let Ok(r) = Regex::new(s) {
            return Some(r)
        } else {
            error!("Regex error, continuing without regex");
        }
    }
    None

}

fn get_id<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<String> {
    debug!("Fetching id from commandline");
    sub.value_of("id").and_then(|s| Some(String::from(s)))
}

/*
 * Checks whether the commandline call was set to run the removal "checked",
 * so if another entry from the store refers to this ID, do not remove the file.
 */
fn run_removal_checking(env: &CommandEnv) -> bool {
    env.matches.is_present("check")
}
<|endoftext|>


<commit_before>
use std::fmt;

use parser::ast;
use parser::ast::{AstKind, AstNode};
use parser::input_stream::{InputStream, StreamPosition};
use parser::precedence::Precedence;
use parser::token_kind::TokenKind;
use parser::tokenizer::{Token, TokenError, TokenLocation, Tokenizer, TokenizerMode,
                        TokenizerPosition};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FullToken {
    kind: TokenKind,
    location: TokenLocation
}
impl FullToken {
    pub fn new(kind: TokenKind, location: TokenLocation) -> FullToken {
        FullToken { kind, location }
    }

    pub fn location(&self) -> &TokenLocation {
        &self.location
    }
}
impl Token for FullToken {
    fn make(kind: TokenKind, location: TokenLocation) -> FullToken {
        FullToken::new(kind, location)
    }
    fn kind(&self) -> TokenKind {
        self.kind
    }
    fn start_offset(&self) -> StreamPosition {
        self.location.start_offset()
    }
    fn write_token(&self, w: &mut fmt::Write) -> Result<(), fmt::Error> {
        write!(w, "Token({}@{})", self.kind.name(), self.location.range_string())
    }
}

pub struct FullTokenizerMode {
}
impl TokenizerMode for FullTokenizerMode {
    type Tok = FullToken;

    fn cares_about_newline(&self) -> bool {
        false
    }
    fn note_newline(&mut self) {}
}

#[derive(Debug, Clone)]
pub enum ParseError {
    Unspecified,
    ErrorToken(FullToken),
    TokenizerError(TokenError),
    UnexpectedToken{expected:TokenKind, got:TokenKind},
    ExpectedVariableName,
    ExpectedCommaOrSemicolon,
    ExpectedExpression
}
pub type ParseResult<T> = Result<T, ParseError>;
pub type MaybeParseResult<T> = ParseResult<Option<T>>;

pub struct AstBuilder<STREAM: InputStream> {
    tokenizer: Tokenizer<STREAM, FullTokenizerMode>
}
impl<STREAM: InputStream> AstBuilder<STREAM> {
    pub fn new(stream: STREAM) -> AstBuilder<STREAM> {
        AstBuilder {
            tokenizer: Tokenizer::new(stream, FullTokenizerMode{})
        }
    }

    pub fn read_and_print_tokens(&mut self) {
        // Just read tokens and print them out until we're done, then return Error.
        loop {
            let token = self.next_token().unwrap();
            println!("Token: {}", token.kind().name());
            if token.kind().is_error() {
                panic!("Got token error: {:?}", self.tokenizer.get_error());
            }
            if token.kind().is_end() {
                break;
            }
        }
    }

    pub fn parse_program(&mut self) -> ParseResult<Box<ast::ProgramNode>> {
        let mut program_node = ast::ProgramNode::new();
        loop {
            match self.try_parse_statement()? {
                Some(boxed_source_element) => {
                    program_node.add_source_element(boxed_source_element);
                }
                None => {
                    break;
                }
            }
        }

        // Must have reached end of stream.
        self.must_expect_token(TokenKind::end())?;

        Ok(Box::new(program_node))
    }

    fn try_parse_statement(&mut self) -> MaybeParseResult<Box<AstNode>> {
        let begin_position = self.mark_position();
        let tok = self.next_token()?;
        if tok.kind().is_open_brace() {
            // FIXME: parse either a block or an object literal.
            // The spec says a '{' at the statement level can only be a block,
            // but practical implementations seem to allow bare object literals.
            return Ok(Some(self.parse_block_statement()?));
        }
        if tok.kind().is_var_keyword() {
            return Ok(Some(self.parse_var_statement()?));
        }
        if tok.kind().is_semicolon() {
            return Ok(Some(Box::new(ast::EmptyStmtNode::new())));
        }
        if tok.kind().is_if_keyword() {
            return Ok(Some(self.parse_if_statement()?));
        }

        if let Some(boxed_expr) = self.try_parse_expression(tok, Precedence::lowest())? {
            return Ok(Some(Box::new(ast::ExprStmtNode::new(boxed_expr))));
        }

        self.rewind_position(begin_position);
        Ok(None)
    }

    fn parse_block_statement(&mut self) -> ParseResult<Box<ast::BlockStmtNode>> {
        // FIXME: Parse list of statements.
        self.must_expect_token(TokenKind::close_brace())?;
        Ok(Box::new(ast::BlockStmtNode::new()))
    }

    fn parse_var_statement(&mut self) -> ParseResult<Box<ast::VarStmtNode>> {
        let mut var_statement = Box::new(ast::VarStmtNode::new());
        loop {
            // FIXME: Support initializer expressions.
            // For now, we match only a VarName ("," VarName)* ";"
            let name_token = match self.expect_get_token(TokenKind::identifier())? {
                Some(token) => token,
                None => { return Err(ParseError::ExpectedVariableName); }
            };
            var_statement.add_variable(name_token);

            let next_tok = self.next_token()?;
            if next_tok.kind().is_semicolon() {
                break;
            }
            if next_tok.kind().is_comma() {
                continue;
            }
            return Err(ParseError::ExpectedCommaOrSemicolon);
        }
        Ok(var_statement)
    }

    fn parse_if_statement(&mut self) -> ParseResult<Box<AstNode>> {
        // FIXME: implement this after expression parsing is supported.
        panic!("parse_if_statement is not implemented.")
    }

    fn parse_expression(&mut self, precedence: Precedence) -> ParseResult<Box<AstNode>> {
        let token = self.next_token()?;
        if let Some(boxed_expr) = self.try_parse_expression(token, precedence)? {
            return Ok(boxed_expr);
        }

        Err(ParseError::ExpectedExpression)
    }

    fn try_parse_expression(&mut self, tok: FullToken, precedence: Precedence)
        -> MaybeParseResult<Box<AstNode>>
    {
        if tok.kind().is_identifier() {
            let name_expr = Box::new(ast::NameExprNode::new(tok));
            return Ok(Some(self.parse_rest_of_expression(name_expr, precedence)?));
        }
        Ok(None)
    }

    fn parse_rest_of_expression(&mut self, left_expr: Box<AstNode>, precedence: Precedence)
        -> ParseResult<Box<AstNode>>
    {
        assert!(left_expr.is_expression());

        let mut cur_expr = left_expr;
        loop {
            let position = self.mark_position();
            let tok = self.next_token()?;
            if tok.kind().is_comma() {
                if precedence >= Precedence::comma() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::comma())?;
                cur_expr = Box::new(ast::CommaExprNode::new(cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_assignment_op() {
                // FIXME: Check that cur_expr is a valid lvalue expression.
                // Return syntax error if not.

                // Assignment associates right-to-left, so we use '>' for precedence
                // instead of '>='.
                if precedence > Precedence::assignment() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::assignment())?;
                cur_expr = Box::new(ast::AssignExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_question() {
                if precedence > Precedence::conditional() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let if_expr = self.parse_expression(Precedence::assignment())?;
                self.must_expect_token(TokenKind::colon())?;
                let else_expr = self.parse_expression(Precedence::assignment())?;
                cur_expr = Box::new(ast::CondExprNode::new(cur_expr, if_expr, else_expr));
                continue;
            }

            if tok.kind().is_logical_or() {
                if precedence >= Precedence::logical_or() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::logical_or())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_logical_and() {
                if precedence >= Precedence::logical_and() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::logical_and())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_bit_or() {
                if precedence >= Precedence::bitwise_or() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::bitwise_or())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_bit_xor() {
                if precedence >= Precedence::bitwise_xor() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::bitwise_xor())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_bit_and() {
                if precedence >= Precedence::bitwise_and() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::bitwise_and())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_equality_op() {
                if precedence >= Precedence::equality() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::equality())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_relational_op() {
                if precedence >= Precedence::relational() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::relational())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_shift_op() {
                if precedence >= Precedence::shift() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::shift())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_plus() || tok.kind().is_minus() {
                if precedence >= Precedence::additive() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::additive())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_star() || tok.kind().is_slash() || tok.kind().is_percent() {
                if precedence >= Precedence::multiplicative() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::multiplicative())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            // Unknown token terminates expression.
            self.rewind_position(position);
            break;
        }

        Ok(cur_expr)
    }

    fn must_expect_token(&mut self, kind: TokenKind) -> ParseResult<()> {
        // Mark the position so we can backtrack.
        let position = self.mark_position();
        let token = self.next_token()?;
        if token.kind() == kind {
            Ok(())
        } else {
            self.rewind_position(position);
            Err(ParseError::UnexpectedToken {expected: kind, got: token.kind()})
        }
    }

    fn expect_token(&mut self, kind: TokenKind) -> ParseResult<bool> {
        // Mark the position so we can backtrack.
        let position = self.mark_position();
        let token = self.next_token()?;
        if token.kind() == kind {
            Ok(true)
        } else {
            self.rewind_position(position);
            Ok(false)
        }
    }
    fn expect_get_token(&mut self, kind: TokenKind) -> ParseResult<Option<FullToken>> {
        // Mark the position so we can backtrack.
        let position = self.mark_position();
        let token = self.next_token()?;
        if token.kind() == kind {
            Ok(Some(token))
        } else {
            self.rewind_position(position);
            Ok(None)
        }
    }

    fn mark_position(&mut self) -> TokenizerPosition {
        self.tokenizer.mark_position()
    }
    fn rewind_position(&mut self, position: TokenizerPosition) {
        self.tokenizer.rewind_position(position)
    }

    fn next_token(&mut self) -> ParseResult<FullToken> {
        self.next_token_impl(/* check_kw = */ true, /* want_newlines = */ false)
    }
    fn next_token_no_keywords(&mut self) -> ParseResult<FullToken> {
        self.next_token_impl(/* check_kw = */ true, /* want_newlines = */ false)
    }

    fn next_token_impl(&mut self, check_kw: bool, want_newlines: bool) -> ParseResult<FullToken> {
        loop {
            let token = self.tokenizer.next_token(/* check_kw = */ true);
            let kind = token.kind();
            // Ignore whitespace and comment and newline tokens.
            if kind.is_whitespace() || kind.is_comment() {
                continue;
            }
            if !want_newlines && kind.is_newline() {
                continue;
            }
            if kind.is_error() {
                return Err(ParseError::ErrorToken(token));
            }
            let kw_str = if check_kw { "kw" } else { "no-kw" };
            let nl_str = if want_newlines { "nl" } else { "no-nl" };
            println!("next_token({}, {}): {}", kw_str, nl_str, token.token_string());
            return Ok(token);
        }
    }
}
<commit_msg>Added skipped_newline flag in AstBuilder.<commit_after>
use std::fmt;

use parser::ast;
use parser::ast::{AstKind, AstNode};
use parser::input_stream::{InputStream, StreamPosition};
use parser::precedence::Precedence;
use parser::token_kind::TokenKind;
use parser::tokenizer::{Token, TokenError, TokenLocation, Tokenizer, TokenizerMode,
                        TokenizerPosition};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FullToken {
    kind: TokenKind,
    location: TokenLocation
}
impl FullToken {
    pub fn new(kind: TokenKind, location: TokenLocation) -> FullToken {
        FullToken { kind, location }
    }

    pub fn location(&self) -> &TokenLocation {
        &self.location
    }
}
impl Token for FullToken {
    fn make(kind: TokenKind, location: TokenLocation) -> FullToken {
        FullToken::new(kind, location)
    }
    fn kind(&self) -> TokenKind {
        self.kind
    }
    fn start_offset(&self) -> StreamPosition {
        self.location.start_offset()
    }
    fn write_token(&self, w: &mut fmt::Write) -> Result<(), fmt::Error> {
        write!(w, "Token({}@{})", self.kind.name(), self.location.range_string())
    }
}

pub struct FullTokenizerMode {
}
impl TokenizerMode for FullTokenizerMode {
    type Tok = FullToken;

    fn cares_about_newline(&self) -> bool {
        false
    }
    fn note_newline(&mut self) {}
}

#[derive(Debug, Clone)]
pub enum ParseError {
    Unspecified,
    ErrorToken(FullToken),
    TokenizerError(TokenError),
    UnexpectedToken{expected:TokenKind, got:TokenKind},
    ExpectedVariableName,
    ExpectedCommaOrSemicolon,
    ExpectedExpression
}
pub type ParseResult<T> = Result<T, ParseError>;
pub type MaybeParseResult<T> = ParseResult<Option<T>>;

pub struct AstBuilder<STREAM: InputStream> {
    tokenizer: Tokenizer<STREAM, FullTokenizerMode>,
    skipped_newline: bool
}
impl<STREAM: InputStream> AstBuilder<STREAM> {
    pub fn new(stream: STREAM) -> AstBuilder<STREAM> {
        AstBuilder {
            tokenizer: Tokenizer::new(stream, FullTokenizerMode{}),
            skipped_newline: false
        }
    }

    pub fn read_and_print_tokens(&mut self) {
        // Just read tokens and print them out until we're done, then return Error.
        loop {
            let token = self.next_token().unwrap();
            println!("Token: {}", token.kind().name());
            if token.kind().is_error() {
                panic!("Got token error: {:?}", self.tokenizer.get_error());
            }
            if token.kind().is_end() {
                break;
            }
        }
    }

    pub fn parse_program(&mut self) -> ParseResult<Box<ast::ProgramNode>> {
        let mut program_node = ast::ProgramNode::new();
        loop {
            match self.try_parse_statement()? {
                Some(boxed_source_element) => {
                    program_node.add_source_element(boxed_source_element);
                }
                None => {
                    break;
                }
            }
        }

        // Must have reached end of stream.
        self.must_expect_token(TokenKind::end())?;

        Ok(Box::new(program_node))
    }

    fn try_parse_statement(&mut self) -> MaybeParseResult<Box<AstNode>> {
        let begin_position = self.mark_position();
        let tok = self.next_token()?;
        if tok.kind().is_open_brace() {
            // FIXME: parse either a block or an object literal.
            // The spec says a '{' at the statement level can only be a block,
            // but practical implementations seem to allow bare object literals.
            return Ok(Some(self.parse_block_statement()?));
        }
        if tok.kind().is_var_keyword() {
            return Ok(Some(self.parse_var_statement()?));
        }
        if tok.kind().is_semicolon() {
            return Ok(Some(Box::new(ast::EmptyStmtNode::new())));
        }
        if tok.kind().is_if_keyword() {
            return Ok(Some(self.parse_if_statement()?));
        }

        if let Some(boxed_expr) = self.try_parse_expression(tok, Precedence::lowest())? {
            return Ok(Some(Box::new(ast::ExprStmtNode::new(boxed_expr))));
        }

        self.rewind_position(begin_position);
        Ok(None)
    }

    fn parse_block_statement(&mut self) -> ParseResult<Box<ast::BlockStmtNode>> {
        // FIXME: Parse list of statements.
        self.must_expect_token(TokenKind::close_brace())?;
        Ok(Box::new(ast::BlockStmtNode::new()))
    }

    fn parse_var_statement(&mut self) -> ParseResult<Box<ast::VarStmtNode>> {
        let mut var_statement = Box::new(ast::VarStmtNode::new());
        loop {
            // FIXME: Support initializer expressions.
            // For now, we match only a VarName ("," VarName)* ";"
            let name_token = match self.expect_get_token(TokenKind::identifier())? {
                Some(token) => token,
                None => { return Err(ParseError::ExpectedVariableName); }
            };
            var_statement.add_variable(name_token);

            let next_tok = self.next_token()?;
            if next_tok.kind().is_semicolon() {
                break;
            }
            if next_tok.kind().is_comma() {
                continue;
            }
            return Err(ParseError::ExpectedCommaOrSemicolon);
        }
        Ok(var_statement)
    }

    fn parse_if_statement(&mut self) -> ParseResult<Box<AstNode>> {
        // FIXME: implement this after expression parsing is supported.
        panic!("parse_if_statement is not implemented.")
    }

    fn parse_expression(&mut self, precedence: Precedence) -> ParseResult<Box<AstNode>> {
        let token = self.next_token()?;
        if let Some(boxed_expr) = self.try_parse_expression(token, precedence)? {
            return Ok(boxed_expr);
        }

        Err(ParseError::ExpectedExpression)
    }

    fn try_parse_expression(&mut self, tok: FullToken, precedence: Precedence)
        -> MaybeParseResult<Box<AstNode>>
    {
        if tok.kind().is_identifier() {
            let name_expr = Box::new(ast::NameExprNode::new(tok));
            return Ok(Some(self.parse_rest_of_expression(name_expr, precedence)?));
        }
        Ok(None)
    }

    fn parse_rest_of_expression(&mut self, left_expr: Box<AstNode>, precedence: Precedence)
        -> ParseResult<Box<AstNode>>
    {
        assert!(left_expr.is_expression());

        let mut cur_expr = left_expr;
        loop {
            let position = self.mark_position();
            let tok = self.next_token()?;
            if tok.kind().is_comma() {
                if precedence >= Precedence::comma() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::comma())?;
                cur_expr = Box::new(ast::CommaExprNode::new(cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_assignment_op() {
                // FIXME: Check that cur_expr is a valid lvalue expression.
                // Return syntax error if not.

                // Assignment associates right-to-left, so we use '>' for precedence
                // instead of '>='.
                if precedence > Precedence::assignment() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::assignment())?;
                cur_expr = Box::new(ast::AssignExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_question() {
                if precedence > Precedence::conditional() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let if_expr = self.parse_expression(Precedence::assignment())?;
                self.must_expect_token(TokenKind::colon())?;
                let else_expr = self.parse_expression(Precedence::assignment())?;
                cur_expr = Box::new(ast::CondExprNode::new(cur_expr, if_expr, else_expr));
                continue;
            }

            if tok.kind().is_logical_or() {
                if precedence >= Precedence::logical_or() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::logical_or())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_logical_and() {
                if precedence >= Precedence::logical_and() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::logical_and())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_bit_or() {
                if precedence >= Precedence::bitwise_or() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::bitwise_or())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_bit_xor() {
                if precedence >= Precedence::bitwise_xor() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::bitwise_xor())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_bit_and() {
                if precedence >= Precedence::bitwise_and() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::bitwise_and())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_equality_op() {
                if precedence >= Precedence::equality() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::equality())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_relational_op() {
                if precedence >= Precedence::relational() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::relational())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_shift_op() {
                if precedence >= Precedence::shift() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::shift())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_plus() || tok.kind().is_minus() {
                if precedence >= Precedence::additive() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::additive())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            if tok.kind().is_star() || tok.kind().is_slash() || tok.kind().is_percent() {
                if precedence >= Precedence::multiplicative() {
                    self.rewind_position(position);
                    return Ok(cur_expr);
                }

                let right_expr = self.parse_expression(Precedence::multiplicative())?;
                cur_expr = Box::new(ast::BinaryOpExprNode::new(tok, cur_expr, right_expr));
                continue;
            }

            // Unknown token terminates expression.
            self.rewind_position(position);
            break;
        }

        Ok(cur_expr)
    }

    fn must_expect_token(&mut self, kind: TokenKind) -> ParseResult<()> {
        // Mark the position so we can backtrack.
        let position = self.mark_position();
        let token = self.next_token()?;
        if token.kind() == kind {
            Ok(())
        } else {
            self.rewind_position(position);
            Err(ParseError::UnexpectedToken {expected: kind, got: token.kind()})
        }
    }

    fn expect_token(&mut self, kind: TokenKind) -> ParseResult<bool> {
        // Mark the position so we can backtrack.
        let position = self.mark_position();
        let token = self.next_token()?;
        if token.kind() == kind {
            Ok(true)
        } else {
            self.rewind_position(position);
            Ok(false)
        }
    }
    fn expect_get_token(&mut self, kind: TokenKind) -> ParseResult<Option<FullToken>> {
        // Mark the position so we can backtrack.
        let position = self.mark_position();
        let token = self.next_token()?;
        if token.kind() == kind {
            Ok(Some(token))
        } else {
            self.rewind_position(position);
            Ok(None)
        }
    }

    fn mark_position(&mut self) -> TokenizerPosition {
        self.tokenizer.mark_position()
    }
    fn rewind_position(&mut self, position: TokenizerPosition) {
        self.tokenizer.rewind_position(position)
    }

    fn next_token(&mut self) -> ParseResult<FullToken> {
        self.next_token_impl(/* check_kw = */ true, /* want_newlines = */ false)
    }
    fn next_token_no_keywords(&mut self) -> ParseResult<FullToken> {
        self.next_token_impl(/* check_kw = */ true, /* want_newlines = */ false)
    }

    fn next_token_impl(&mut self, check_kw: bool, want_newlines: bool) -> ParseResult<FullToken> {
        self.skipped_newline = false;
        loop {
            let token = self.tokenizer.next_token(/* check_kw = */ true);
            let kind = token.kind();
            // Ignore whitespace and comment and newline tokens.
            if kind.is_whitespace() || kind.is_comment() {
                continue;
            }
            if !want_newlines && kind.is_newline() {
                self.skipped_newline = true;
                continue;
            }
            if kind.is_error() {
                return Err(ParseError::ErrorToken(token));
            }
            let kw_str = if check_kw { "kw" } else { "no-kw" };
            let nl_str = if want_newlines { "nl" } else { "no-nl" };
            println!("next_token({}, {}): {}", kw_str, nl_str, token.token_string());
            return Ok(token);
        }
    }
}
<|endoftext|>


<commit_before>//! Primitive generation.
//!
//! This module provides a generic iterator and traits for generating streams
//! of geometric and topological data for primitives like cubes and spheres.

use std::ops::Range;

use crate::primitive::topology::Polygonal;

pub struct Generate<'a, G, S, P>
where
    G: 'a,
{
    generator: &'a G,
    state: S,
    range: Range<usize>,
    f: fn(&'a G, &S, usize) -> P,
}

impl<'a, G, S, P> Generate<'a, G, S, P>
where
    G: 'a,
{
    pub(in crate::primitive) fn new(
        generator: &'a G,
        state: S,
        n: usize,
        f: fn(&'a G, &S, usize) -> P,
    ) -> Self {
        Generate {
            generator,
            state,
            range: 0..n,
            f,
        }
    }
}

impl<'a, G, S, P> Iterator for Generate<'a, G, S, P>
where
    G: 'a,
{
    type Item = P;

    fn next(&mut self) -> Option<Self::Item> {
        self.range
            .next()
            .map(|index| (self.f)(self.generator, &self.state, index))
    }
}

/// Vertex generating primitive.
pub trait VertexGenerator {}

/// Polygon generating primitive.
pub trait PolygonGenerator {
    /// Gets the number of unique polygons that comprise a primitive.
    fn polygon_count(&self) -> usize;
}

pub trait PositionGenerator {
    type State: Default;
}

pub trait PositionVertexGenerator: PositionGenerator + VertexGenerator {
    type Output;

    fn vertex_with_position_from(&self, state: &Self::State, index: usize) -> Self::Output;

    /// Gets the number of unique vertices with position data that comprise a primitive.
    fn vertex_with_position_count(&self) -> usize;
}

/// Functions for generating vertices with position data.
pub trait VerticesWithPosition<P>: PositionGenerator + Sized {
    /// Provides an iterator over the set of unique vertices with position
    /// data.
    ///
    /// This can be paired with functions from `IndicesForPosition` to index
    /// the set of positions.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plexus::prelude::*;
    /// use plexus::primitive::sphere::UvSphere;
    ///
    /// let sphere = UvSphere::new(8, 8);
    /// let vertices = sphere.vertices_with_position().collect::<Vec<_>>();
    /// let indices = sphere
    ///     .indices_for_position()
    ///     .triangulate()
    ///     .vertices()
    ///     .collect::<Vec<_>>();
    /// ```
    fn vertices_with_position(&self) -> Generate<Self, Self::State, P> {
        self.vertices_with_position_from(Default::default())
    }

    /// Provides an iterator over the set of unique vertices with position data
    /// using the provided state.
    ///
    /// This can be paired with functions from `IndicesForPosition` to index
    /// the set of positions.
    ///
    /// State typically dictates the scale of the generated positions, using a
    /// unit width, radius, etc.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plexus::prelude::*;
    /// use plexus::primitive::cube::{Bounds, Cube};
    ///
    /// let cube = Cube::new();
    /// let vertices = cube
    ///     .vertices_with_position_from(Bounds::with_width(3.0.into()))
    ///     .collect::<Vec<_>>();
    /// let indices = cube
    ///     .indices_for_position()
    ///     .triangulate()
    ///     .vertices()
    ///     .collect::<Vec<_>>();
    /// ```
    fn vertices_with_position_from(&self, state: Self::State) -> Generate<Self, Self::State, P>;
}

impl<G, P> VerticesWithPosition<P> for G
where
    G: PositionVertexGenerator<Output = P>,
{
    fn vertices_with_position_from(&self, state: Self::State) -> Generate<Self, Self::State, P> {
        Generate::new(
            self,
            state,
            self.vertex_with_position_count(),
            G::vertex_with_position_from,
        )
    }
}

pub trait PositionIndexGenerator: PolygonGenerator + PositionVertexGenerator {
    type Output: Polygonal<Vertex = usize>;

    fn index_for_position(&self, index: usize) -> <Self as PositionIndexGenerator>::Output;
}

pub trait IndicesForPosition: PositionIndexGenerator + Sized {
    fn indices_for_position(&self) -> Generate<Self, (), <Self as PositionIndexGenerator>::Output> {
        Generate::new(self, (), self.polygon_count(), |generator, _, index| {
            generator.index_for_position(index)
        })
    }
}

impl<G> IndicesForPosition for G where G: PositionIndexGenerator + Sized {}

pub trait PositionPolygonGenerator: PolygonGenerator + PositionGenerator {
    type Output: Polygonal;

    fn polygon_with_position_from(&self, state: &Self::State, index: usize) -> Self::Output;
}

/// Functions for generating polygons with position data.
pub trait PolygonsWithPosition<P>: PositionGenerator + Sized {
    /// Provides an iterator over the set of unique polygons with position
    /// data.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plexus::prelude::*;
    /// use plexus::primitive::index::HashIndexer;
    /// use plexus::primitive::sphere::UvSphere;
    ///
    /// let (indices, positions) = UvSphere::new(8, 8)
    ///     .polygons_with_position()
    ///     .index_vertices(HashIndexer::default());
    /// ```
    fn polygons_with_position(&self) -> Generate<Self, Self::State, P> {
        self.polygons_with_position_from(Default::default())
    }

    /// Provides an iterator over the set of unique polygons with position data
    /// using the provided state.
    ///
    /// State typically dictates the scale of the generated positions, using a
    /// unit width, radius, etc.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plexus::prelude::*;
    /// use plexus::primitive::cube::{Bounds, Cube};
    /// use plexus::primitive::index::HashIndexer;
    ///
    /// let (indices, positions) = Cube::new()
    ///     .polygons_with_position_from(Bounds::unit_radius())
    ///     .index_vertices(HashIndexer::default());
    /// ```
    fn polygons_with_position_from(&self, state: Self::State) -> Generate<Self, Self::State, P>;
}

impl<G, P> PolygonsWithPosition<P> for G
where
    G: PositionPolygonGenerator<Output = P>,
    P: Polygonal,
{
    fn polygons_with_position_from(&self, state: Self::State) -> Generate<Self, Self::State, P> {
        Generate::new(
            self,
            state,
            self.polygon_count(),
            G::polygon_with_position_from,
        )
    }
}

pub trait UvMapGenerator {
    type State: Default;
}

pub trait UvMapPolygonGenerator: PolygonGenerator + UvMapGenerator {
    type Output: Polygonal;

    fn polygon_with_uv_map_from(
        &self,
        state: &Self::State,
        index: usize,
    ) -> <Self as UvMapPolygonGenerator>::Output;
}

/// Functions for generating polygons with UV-mapping (texture coordinate)
/// data.
pub trait PolygonsWithUvMap<P>: Sized + UvMapGenerator {
    /// Provides an iterator over the set of unique polygons with UV-mapping
    /// data.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plexus::prelude::*;
    /// use plexus::primitive;
    /// use plexus::primitive::cube::Cube;
    /// use plexus::primitive::index::HashIndexer;
    ///
    /// let cube = Cube::new();
    /// let (indices, positions) =
    ///     primitive::zip_vertices((cube.polygons_with_position(), cube.polygons_with_uv_map()))
    ///         .index_vertices(HashIndexer::default());
    /// ```
    fn polygons_with_uv_map(&self) -> Generate<Self, Self::State, P> {
        self.polygons_with_uv_map_from(Default::default())
    }

    /// Provides an iterator over the set of unique polygons with UV-mapping
    /// data using the provided state.
    fn polygons_with_uv_map_from(&self, state: Self::State) -> Generate<Self, Self::State, P>;
}

impl<G, P> PolygonsWithUvMap<P> for G
where
    G: PolygonGenerator + UvMapPolygonGenerator<Output = P>,
    P: Polygonal,
{
    fn polygons_with_uv_map_from(&self, state: Self::State) -> Generate<Self, Self::State, P> {
        Generate::new(
            self,
            state,
            self.polygon_count(),
            G::polygon_with_uv_map_from,
        )
    }
}
<commit_msg>Remove unneeded type parameter from generator traits.<commit_after>//! Primitive generation.
//!
//! This module provides a generic iterator and traits for generating streams
//! of geometric and topological data for primitives like cubes and spheres.

use std::ops::Range;

use crate::primitive::topology::Polygonal;

pub struct Generate<'a, G, S, P>
where
    G: 'a,
{
    generator: &'a G,
    state: S,
    range: Range<usize>,
    f: fn(&'a G, &S, usize) -> P,
}

impl<'a, G, S, P> Generate<'a, G, S, P>
where
    G: 'a,
{
    pub(in crate::primitive) fn new(
        generator: &'a G,
        state: S,
        n: usize,
        f: fn(&'a G, &S, usize) -> P,
    ) -> Self {
        Generate {
            generator,
            state,
            range: 0..n,
            f,
        }
    }
}

impl<'a, G, S, P> Iterator for Generate<'a, G, S, P>
where
    G: 'a,
{
    type Item = P;

    fn next(&mut self) -> Option<Self::Item> {
        self.range
            .next()
            .map(|index| (self.f)(self.generator, &self.state, index))
    }
}

/// Vertex generating primitive.
pub trait VertexGenerator {}

/// Polygon generating primitive.
pub trait PolygonGenerator {
    /// Gets the number of unique polygons that comprise a primitive.
    fn polygon_count(&self) -> usize;
}

pub trait PositionGenerator {
    type State: Default;
}

pub trait PositionVertexGenerator: PositionGenerator + VertexGenerator {
    type Output;

    fn vertex_with_position_from(&self, state: &Self::State, index: usize) -> Self::Output;

    /// Gets the number of unique vertices with position data that comprise a primitive.
    fn vertex_with_position_count(&self) -> usize;
}

/// Functions for generating vertices with position data.
pub trait VerticesWithPosition: PositionVertexGenerator + Sized {
    /// Provides an iterator over the set of unique vertices with position
    /// data.
    ///
    /// This can be paired with functions from `IndicesForPosition` to index
    /// the set of positions.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plexus::prelude::*;
    /// use plexus::primitive::sphere::UvSphere;
    ///
    /// let sphere = UvSphere::new(8, 8);
    /// let vertices = sphere.vertices_with_position().collect::<Vec<_>>();
    /// let indices = sphere
    ///     .indices_for_position()
    ///     .triangulate()
    ///     .vertices()
    ///     .collect::<Vec<_>>();
    /// ```
    fn vertices_with_position(
        &self,
    ) -> Generate<Self, Self::State, <Self as PositionVertexGenerator>::Output> {
        self.vertices_with_position_from(Default::default())
    }

    /// Provides an iterator over the set of unique vertices with position data
    /// using the provided state.
    ///
    /// This can be paired with functions from `IndicesForPosition` to index
    /// the set of positions.
    ///
    /// State typically dictates the scale of the generated positions, using a
    /// unit width, radius, etc.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plexus::prelude::*;
    /// use plexus::primitive::cube::{Bounds, Cube};
    ///
    /// let cube = Cube::new();
    /// let vertices = cube
    ///     .vertices_with_position_from(Bounds::with_width(3.0.into()))
    ///     .collect::<Vec<_>>();
    /// let indices = cube
    ///     .indices_for_position()
    ///     .triangulate()
    ///     .vertices()
    ///     .collect::<Vec<_>>();
    /// ```
    fn vertices_with_position_from(
        &self,
        state: Self::State,
    ) -> Generate<Self, Self::State, <Self as PositionVertexGenerator>::Output> {
        Generate::new(
            self,
            state,
            self.vertex_with_position_count(),
            Self::vertex_with_position_from,
        )
    }
}

impl<G> VerticesWithPosition for G where G: PositionVertexGenerator + Sized {}

pub trait PositionIndexGenerator: PolygonGenerator + PositionVertexGenerator {
    type Output: Polygonal<Vertex = usize>;

    fn index_for_position(&self, index: usize) -> <Self as PositionIndexGenerator>::Output;
}

pub trait IndicesForPosition: PositionIndexGenerator + Sized {
    fn indices_for_position(&self) -> Generate<Self, (), <Self as PositionIndexGenerator>::Output> {
        Generate::new(self, (), self.polygon_count(), |generator, _, index| {
            generator.index_for_position(index)
        })
    }
}

impl<G> IndicesForPosition for G where G: PositionIndexGenerator + Sized {}

pub trait PositionPolygonGenerator: PolygonGenerator + PositionGenerator {
    type Output: Polygonal;

    fn polygon_with_position_from(&self, state: &Self::State, index: usize) -> Self::Output;
}

/// Functions for generating polygons with position data.
pub trait PolygonsWithPosition: PositionPolygonGenerator + Sized {
    /// Provides an iterator over the set of unique polygons with position
    /// data.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plexus::prelude::*;
    /// use plexus::primitive::index::HashIndexer;
    /// use plexus::primitive::sphere::UvSphere;
    ///
    /// let (indices, positions) = UvSphere::new(8, 8)
    ///     .polygons_with_position()
    ///     .index_vertices(HashIndexer::default());
    /// ```
    fn polygons_with_position(
        &self,
    ) -> Generate<Self, Self::State, <Self as PositionPolygonGenerator>::Output> {
        self.polygons_with_position_from(Default::default())
    }

    /// Provides an iterator over the set of unique polygons with position data
    /// using the provided state.
    ///
    /// State typically dictates the scale of the generated positions, using a
    /// unit width, radius, etc.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plexus::prelude::*;
    /// use plexus::primitive::cube::{Bounds, Cube};
    /// use plexus::primitive::index::HashIndexer;
    ///
    /// let (indices, positions) = Cube::new()
    ///     .polygons_with_position_from(Bounds::unit_radius())
    ///     .index_vertices(HashIndexer::default());
    /// ```
    fn polygons_with_position_from(
        &self,
        state: Self::State,
    ) -> Generate<Self, Self::State, <Self as PositionPolygonGenerator>::Output> {
        Generate::new(
            self,
            state,
            self.polygon_count(),
            Self::polygon_with_position_from,
        )
    }
}

impl<G> PolygonsWithPosition for G where G: PositionPolygonGenerator {}

pub trait UvMapGenerator {
    type State: Default;
}

pub trait UvMapPolygonGenerator: PolygonGenerator + UvMapGenerator {
    type Output: Polygonal;

    fn polygon_with_uv_map_from(
        &self,
        state: &Self::State,
        index: usize,
    ) -> <Self as UvMapPolygonGenerator>::Output;
}

/// Functions for generating polygons with UV-mapping (texture coordinate)
/// data.
pub trait PolygonsWithUvMap: Sized + UvMapPolygonGenerator {
    /// Provides an iterator over the set of unique polygons with UV-mapping
    /// data.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use plexus::prelude::*;
    /// use plexus::primitive;
    /// use plexus::primitive::cube::Cube;
    /// use plexus::primitive::index::HashIndexer;
    ///
    /// let cube = Cube::new();
    /// let (indices, positions) =
    ///     primitive::zip_vertices((cube.polygons_with_position(), cube.polygons_with_uv_map()))
    ///         .index_vertices(HashIndexer::default());
    /// ```
    fn polygons_with_uv_map(
        &self,
    ) -> Generate<Self, Self::State, <Self as UvMapPolygonGenerator>::Output> {
        self.polygons_with_uv_map_from(Default::default())
    }

    /// Provides an iterator over the set of unique polygons with UV-mapping
    /// data using the provided state.
    fn polygons_with_uv_map_from(
        &self,
        state: Self::State,
    ) -> Generate<Self, Self::State, <Self as UvMapPolygonGenerator>::Output> {
        Generate::new(
            self,
            state,
            self.polygon_count(),
            Self::polygon_with_uv_map_from,
        )
    }
}

impl<G> PolygonsWithUvMap for G where G: UvMapPolygonGenerator {}
<|endoftext|>


<commit_before>#[cfg(feature = "egl")]
use std::{cell::RefCell, rc::Rc, sync::mpsc::Sender};

#[cfg(feature = "udev")]
use smithay::backend::renderer::{Renderer, Texture};
#[cfg(feature = "udev")]
use smithay::reexports::nix::libc::dev_t;
#[cfg(feature = "udev")]
use std::collections::HashMap;

#[cfg(feature = "egl")]
use smithay::backend::egl::{display::EGLBufferReader, BufferAccessError as EGLBufferAccessError, EGLBuffer};
use smithay::{
    reexports::wayland_server::protocol::wl_buffer::WlBuffer,
    wayland::shm::{with_buffer_contents as shm_buffer_contents, BufferAccessError},
};

/// Utilities for working with `WlBuffer`s.
#[derive(Clone)]
pub struct BufferUtils {
    #[cfg(feature = "egl")]
    egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
    log: ::slog::Logger,
}

impl BufferUtils {
    /// Creates a new `BufferUtils`.
    #[cfg(feature = "egl")]
    pub fn new(egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>, log: ::slog::Logger) -> Self {
        Self {
            egl_buffer_reader,
            log,
        }
    }

    /// Creates a new `BufferUtils`.
    #[cfg(not(feature = "egl"))]
    pub fn new(log: ::slog::Logger) -> Self {
        Self { log }
    }

    /// Returns the dimensions of an image stored in the buffer.
    #[cfg(feature = "egl")]
    pub fn dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> {
        // Try to retrieve the EGL dimensions of this buffer, and, if that fails, the shm dimensions.
        self.egl_buffer_reader
            .borrow()
            .as_ref()
            .and_then(|display| display.egl_buffer_dimensions(buffer))
            .or_else(|| self.shm_buffer_dimensions(buffer).ok())
    }

    /// Returns the dimensions of an image stored in the buffer.
    #[cfg(not(feature = "egl"))]
    pub fn dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> {
        self.shm_buffer_dimensions(buffer).ok()
    }

    /// Returns the dimensions of an image stored in the shm buffer.
    fn shm_buffer_dimensions(&self, buffer: &WlBuffer) -> Result<(i32, i32), BufferAccessError> {
        shm_buffer_contents(buffer, |_, data| (data.width, data.height)).map_err(|err| {
            warn!(self.log, "Unable to load buffer contents"; "err" => format!("{:?}", err));
            err
        })
    }

    #[cfg(feature = "egl")]
    pub fn load_buffer<T>(&self, buffer: WlBuffer) -> Result<BufferTextures<T>, WlBuffer> {
        let egl_buffer = if let Some(reader) = &self.egl_buffer_reader.borrow().as_ref() {
            match reader.egl_buffer_contents(&buffer) {
                Ok(egl) => Some(egl),
                Err(EGLBufferAccessError::NotManaged(_)) => None,
                Err(err) => {
                    error!(self.log, "EGL error"; "err" => format!("{:?}", err));
                    return Err(buffer);
                }
            }
        } else { None };

        Ok(BufferTextures {
            buffer,
            textures: HashMap::new(),
            callbacks: HashMap::new(),
            egl: egl_buffer, // I guess we need to keep this alive ?
        })
    }

    #[cfg(not(feature = "egl"))]
    pub fn load_buffer<T>(&self, buffer: WlBuffer) -> Result<BufferTextures<T>, WlBuffer> {
        Ok(BufferTextures {
            buffer,
            textures: HashMap::new(),
        })
    }
}

#[cfg(feature = "udev")]
pub struct BufferTextures<T> {
    buffer: WlBuffer,
    pub textures: HashMap<dev_t, T>,
    callbacks: HashMap<dev_t, Sender<T>>,
    #[cfg(feature = "egl")]
    egl: Option<EGLBuffer>,
}

#[cfg(feature = "udev")]
impl<T: Texture> BufferTextures<T> {
    #[cfg(feature = "egl")]
    pub fn load_texture<'a, R: Renderer<TextureId = T>>(
        &'a mut self,
        id: u64,
        renderer: &mut R,
        texture_destruction_callback: &Sender<T>,
    ) -> Result<&'a T, R::Error> {
        if self.textures.contains_key(&id) {
            return Ok(&self.textures[&id]);
        }

        if let Some(buffer) = self.egl.as_ref() {
            //EGL buffer
            let texture = renderer.import_egl(&buffer)?;
            if let Some(old_texture) = self.textures.insert(id, texture) {
                let _ = renderer.destroy_texture(old_texture);
            }
            self.callbacks.insert(id, texture_destruction_callback.clone());
            Ok(&self.textures[&id])
        } else {
            self.load_shm_texture(id, renderer, texture_destruction_callback)
        }
    }

    #[cfg(not(feature = "egl"))]
    pub fn load_texture<'a, R: Renderer<Texture = T>>(
        &'a mut self,
        id: u64,
        renderer: &mut R,
        texture_destruction_callback: &Sender<T>,
    ) -> Result<&'a T, R::Error> {
        if self.textures.contains_key(&id) {
            return Ok(&self.textures[&id]);
        }

        self.load_shm_texture(id, renderer, texture_destruction_callback)
    }

    fn load_shm_texture<'a, R: Renderer<TextureId = T>>(
        &'a mut self,
        id: u64,
        renderer: &mut R,
        texture_destruction_callback: &Sender<T>,
    ) -> Result<&'a T, R::Error> {
        let texture = renderer.import_shm(&self.buffer)?;

        if let Some(old_texture) = self.textures.insert(id, texture) {
            let _ = renderer.destroy_texture(old_texture)?;
        }
        self.callbacks.insert(id, texture_destruction_callback.clone());
        Ok(&self.textures[&id])
    }
}

#[cfg(feature = "udev")]
impl<T> Drop for BufferTextures<T> {
    fn drop(&mut self) {
        self.buffer.release();
        for (id, texture) in self.textures.drain() {
            self.callbacks.get(&id).unwrap().send(texture).unwrap();
        }
    }
}
<commit_msg>fix: Compiling anvil without egl<commit_after>#[cfg(feature = "egl")]
use std::{cell::RefCell, rc::Rc};
use std::sync::mpsc::Sender;

#[cfg(feature = "udev")]
use smithay::backend::renderer::{Renderer, Texture};
#[cfg(feature = "udev")]
use smithay::reexports::nix::libc::dev_t;
#[cfg(feature = "udev")]
use std::collections::HashMap;

#[cfg(feature = "egl")]
use smithay::backend::egl::{display::EGLBufferReader, BufferAccessError as EGLBufferAccessError, EGLBuffer};
use smithay::{
    reexports::wayland_server::protocol::wl_buffer::WlBuffer,
    wayland::shm::{with_buffer_contents as shm_buffer_contents, BufferAccessError},
};

/// Utilities for working with `WlBuffer`s.
#[derive(Clone)]
pub struct BufferUtils {
    #[cfg(feature = "egl")]
    egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
    log: ::slog::Logger,
}

impl BufferUtils {
    /// Creates a new `BufferUtils`.
    #[cfg(feature = "egl")]
    pub fn new(egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>, log: ::slog::Logger) -> Self {
        Self {
            egl_buffer_reader,
            log,
        }
    }

    /// Creates a new `BufferUtils`.
    #[cfg(not(feature = "egl"))]
    pub fn new(log: ::slog::Logger) -> Self {
        Self { log }
    }

    /// Returns the dimensions of an image stored in the buffer.
    #[cfg(feature = "egl")]
    pub fn dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> {
        // Try to retrieve the EGL dimensions of this buffer, and, if that fails, the shm dimensions.
        self.egl_buffer_reader
            .borrow()
            .as_ref()
            .and_then(|display| display.egl_buffer_dimensions(buffer))
            .or_else(|| self.shm_buffer_dimensions(buffer).ok())
    }

    /// Returns the dimensions of an image stored in the buffer.
    #[cfg(not(feature = "egl"))]
    pub fn dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> {
        self.shm_buffer_dimensions(buffer).ok()
    }

    /// Returns the dimensions of an image stored in the shm buffer.
    fn shm_buffer_dimensions(&self, buffer: &WlBuffer) -> Result<(i32, i32), BufferAccessError> {
        shm_buffer_contents(buffer, |_, data| (data.width, data.height)).map_err(|err| {
            warn!(self.log, "Unable to load buffer contents"; "err" => format!("{:?}", err));
            err
        })
    }

    #[cfg(feature = "egl")]
    pub fn load_buffer<T>(&self, buffer: WlBuffer) -> Result<BufferTextures<T>, WlBuffer> {
        let egl_buffer = if let Some(reader) = &self.egl_buffer_reader.borrow().as_ref() {
            match reader.egl_buffer_contents(&buffer) {
                Ok(egl) => Some(egl),
                Err(EGLBufferAccessError::NotManaged(_)) => None,
                Err(err) => {
                    error!(self.log, "EGL error"; "err" => format!("{:?}", err));
                    return Err(buffer);
                }
            }
        } else { None };

        Ok(BufferTextures {
            buffer,
            textures: HashMap::new(),
            callbacks: HashMap::new(),
            egl: egl_buffer, // I guess we need to keep this alive ?
        })
    }

    #[cfg(not(feature = "egl"))]
    pub fn load_buffer<T>(&self, buffer: WlBuffer) -> Result<BufferTextures<T>, WlBuffer> {
        Ok(BufferTextures {
            buffer,
            textures: HashMap::new(),
            callbacks: HashMap::new(),
        })
    }
}

#[cfg(feature = "udev")]
pub struct BufferTextures<T> {
    buffer: WlBuffer,
    pub textures: HashMap<dev_t, T>,
    callbacks: HashMap<dev_t, Sender<T>>,
    #[cfg(feature = "egl")]
    egl: Option<EGLBuffer>,
}

#[cfg(feature = "udev")]
impl<T: Texture> BufferTextures<T> {
    #[cfg(feature = "egl")]
    pub fn load_texture<'a, R: Renderer<TextureId = T>>(
        &'a mut self,
        id: u64,
        renderer: &mut R,
        texture_destruction_callback: &Sender<T>,
    ) -> Result<&'a T, R::Error> {
        if self.textures.contains_key(&id) {
            return Ok(&self.textures[&id]);
        }

        if let Some(buffer) = self.egl.as_ref() {
            //EGL buffer
            let texture = renderer.import_egl(&buffer)?;
            if let Some(old_texture) = self.textures.insert(id, texture) {
                let _ = renderer.destroy_texture(old_texture);
            }
            self.callbacks.insert(id, texture_destruction_callback.clone());
            Ok(&self.textures[&id])
        } else {
            self.load_shm_texture(id, renderer, texture_destruction_callback)
        }
    }

    #[cfg(not(feature = "egl"))]
    pub fn load_texture<'a, R: Renderer<TextureId = T>>(
        &'a mut self,
        id: u64,
        renderer: &mut R,
        texture_destruction_callback: &Sender<T>,
    ) -> Result<&'a T, R::Error> {
        if self.textures.contains_key(&id) {
            return Ok(&self.textures[&id]);
        }

        self.load_shm_texture(id, renderer, texture_destruction_callback)
    }

    fn load_shm_texture<'a, R: Renderer<TextureId = T>>(
        &'a mut self,
        id: u64,
        renderer: &mut R,
        texture_destruction_callback: &Sender<T>,
    ) -> Result<&'a T, R::Error> {
        let texture = renderer.import_shm(&self.buffer)?;

        if let Some(old_texture) = self.textures.insert(id, texture) {
            let _ = renderer.destroy_texture(old_texture)?;
        }
        self.callbacks.insert(id, texture_destruction_callback.clone());
        Ok(&self.textures[&id])
    }
}

#[cfg(feature = "udev")]
impl<T> Drop for BufferTextures<T> {
    fn drop(&mut self) {
        self.buffer.release();
        for (id, texture) in self.textures.drain() {
            self.callbacks.get(&id).unwrap().send(texture).unwrap();
        }
    }
}
<|endoftext|>


<commit_before>
use std::path::{PathBuf, Path};
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::fs::File;
use std::io::Write;
use std::io::BufWriter;
use std::io;
use std::borrow::Borrow;
use std::borrow::BorrowMut;
use std::rc::Rc;
use std::sync::{Arc, Mutex, RwLock, MutexGuard, RwLockWriteGuard, RwLockReadGuard};
use std::fmt;
use std::ops::Deref;
use std::cell::RefCell;
use core::error::*;
use rand::{thread_rng, Rng};
use fst::raw::MmapReadOnly;
use rustc_serialize::json;
use atomicwrites;
use tempdir::TempDir;

#[derive(Clone, Debug)]
pub struct SegmentId(pub String);

pub fn generate_segment_name() -> SegmentId {
    static CHARS: &'static [u8] = b"abcdefghijklmnopqrstuvwxyz0123456789";
    let random_name: String = (0..8)
            .map(|_| thread_rng().choose(CHARS).unwrap().clone() as char)
            .collect();
    SegmentId( String::from("_") + &random_name)
}

// #[derive()]
#[derive(Clone,Debug,RustcDecodable, RustcEncodable)]
pub struct DirectoryMeta {
    segments: Vec<String>
}

impl DirectoryMeta {
    fn new() -> DirectoryMeta {
        DirectoryMeta {
            segments: Vec::new()
        }
    }
}

impl fmt::Debug for Directory {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
       write!(f, "Directory({:?})", self.inner_directory.read().unwrap().index_path)
   }
}

fn open_mmap(full_path: &PathBuf) -> Result<MmapReadOnly> {
    match MmapReadOnly::open_path(full_path.clone()) {
        Ok(mmapped_file) => Ok(mmapped_file),
        Err(ioerr) => {
            // TODO add file
            let error_msg = format!("Read-Only MMap of {:?} failed", full_path);
            return Err(Error::IOError(ioerr.kind(), error_msg));
        }
    }
}

fn sync_file(filepath: &PathBuf) -> Result<()> {
    match File::open(filepath.clone()) {
        Ok(fd) => {
            match fd.sync_all() {
                Err(err) => Err(Error::IOError(err.kind(), format!("Failed to sync {:?}", filepath))),
                _ => Ok(())
            }
        },
        Err(err) => Err(Error::IOError(err.kind(), format!("Cause: {:?}", err)))
    }
}

#[derive(Clone)]
pub struct Directory {
    inner_directory: Arc<RwLock<InnerDirectory>>,
}

impl Directory {

    fn get_write(&mut self) -> Result<RwLockWriteGuard<InnerDirectory>> {
        match self.inner_directory.as_ref().write() {
            Ok(dir) =>
                Ok(dir),
            Err(e) =>
                Err(Error::LockError(format!("Could not acquire write lock on directory. {:?}", e)))
        }
    }

    fn get_read(&self) -> Result<RwLockReadGuard<InnerDirectory>> {
        match self.inner_directory.as_ref().read() {
            Ok(dir) =>
                Ok(dir),
            Err(e) =>
                Err(Error::LockError(format!("Could not acquire read lock on directory. {:?}", e)))
        }
    }

    pub fn publish_segment(&mut self, segment: Segment) -> Result<()> {
        return try!(self.get_write()).publish_segment(segment);
    }

    pub fn open(filepath: &Path) -> Result<Directory> {
        let inner_directory = try!(InnerDirectory::open(filepath));
        Ok(Directory {
            inner_directory: Arc::new(RwLock::new(inner_directory)),
        })
    }

    pub fn from_tempdir() -> Result<Directory> {
        let inner_directory = try!(InnerDirectory::from_tempdir());
        Ok(Directory {
            inner_directory: Arc::new(RwLock::new(inner_directory)),
        })
    }

    pub fn load_metas(&self,) -> Result<()> {
        match self.inner_directory.as_ref().read() {
            Ok(dir) => dir.load_metas(),
            Err(e) => Err(Error::LockError(format!("Could not get read lock {:?} for directory", e)))
        }
    }

    pub fn sync(&mut self, segment: Segment) -> Result<()> {
        try!(self.get_write()).sync(segment)
    }

    pub fn segments(&self,) -> Vec<Segment> {
        match self.inner_directory.as_ref().read() {
            Ok(inner) => inner
                    .segment_ids()
                    .into_iter()
                    .map(|segment_id| self.segment(&segment_id))
                    .collect(),
            Err(e) => {
                //Err(Error::LockError(format!("Could not obtain read lock for {:?}", self)))
                // TODO make it return a result
                panic!("Could not work");
            }
        }

    }

    pub fn segment(&self, segment_id: &SegmentId) -> Segment {
        Segment {
            directory: self.clone(),
            segment_id: segment_id.clone()
        }
    }

    pub fn new_segment(&self,) -> Segment {
        // TODO check it does not exists
        self.segment(&generate_segment_name())
    }

    fn open_writable(&self, relative_path: &PathBuf) -> Result<File> {
        try!(self.get_read()).open_writable(relative_path)
    }

    fn mmap(&self, relative_path: &PathBuf) -> Result<MmapReadOnly> {
        try!(self.get_read()).mmap(relative_path)
    }
}

struct InnerDirectory {
    index_path: PathBuf,
    mmap_cache: RefCell<HashMap<PathBuf, MmapReadOnly>>,
    metas: DirectoryMeta,
    _temp_directory: Option<TempDir>,
}

fn create_tempdir() -> Result<TempDir> {
    let tempdir_res = TempDir::new("index");
    match tempdir_res {
        Ok(tempdir) => Ok(tempdir),
        Err(_) => Err(Error::FileNotFound(String::from("Could not create temp directory")))
    }
}

impl InnerDirectory {

    // TODO find a rusty way to hide that, while keeping
    // it visible for IndexWriters.
    pub fn publish_segment(&mut self, segment: Segment) -> Result<()> {
        self.metas.segments.push(segment.segment_id.0.clone());
        // TODO use logs
        self.save_metas()
    }

    pub fn open(filepath: &Path) -> Result<InnerDirectory> {
        let mut directory = InnerDirectory {
            index_path: PathBuf::from(filepath),
            mmap_cache: RefCell::new(HashMap::new()),
            metas: DirectoryMeta::new(),
            _temp_directory: None,
        };
        try!(directory.load_metas()); //< does the directory already exists?
        Ok(directory)
    }

    pub fn segment_ids(&self,) -> Vec<SegmentId> {
        self.metas
            .segments
            .iter()
            .cloned()
            .map(SegmentId)
            .collect()
    }

    pub fn from_tempdir() -> Result<InnerDirectory> {
        let tempdir = try!(create_tempdir());
        let tempdir_path = PathBuf::from(tempdir.path());
        let mut directory = InnerDirectory {
            index_path: PathBuf::from(tempdir_path),
            mmap_cache: RefCell::new(HashMap::new()),
            metas: DirectoryMeta::new(),
            _temp_directory: Some(tempdir)
        };
        //< does the directory already exists?
        try!(directory.load_metas());
        Ok(directory)
    }

    pub fn load_metas(&self,) -> Result<()> {
        // TODO load segment info
        Ok(())
    }

    fn meta_filepath(&self,) -> PathBuf {
        self.resolve_path(&PathBuf::from("meta.json"))
    }

    pub fn save_metas(&self,) -> Result<()> {
        let encoded = json::encode(&self.metas).unwrap();
        let meta_filepath = self.meta_filepath();
        let meta_file = atomicwrites::AtomicFile::new(meta_filepath, atomicwrites::AllowOverwrite);
        let write_result = meta_file.write(|f| {
            f.write_all(encoded.as_bytes())
        });
        match write_result {
            Ok(_) => { Ok(()) },
            Err(ioerr) => Err(Error::IOError(ioerr.kind(), format!("Failed to write meta file : {:?}", ioerr))),
        }
    }

    pub fn sync(&mut self, segment: Segment) -> Result<()> {
        for component in [SegmentComponent::POSTINGS, SegmentComponent::TERMS].iter() {
            let relative_path = segment.relative_path(component);
            let full_path = self.resolve_path(&relative_path);
            try!(sync_file(&full_path));
        }
        // syncing the directory itself
        try!(sync_file(&self.index_path));
        Ok(())
    }

    fn resolve_path(&self, relative_path: &PathBuf) -> PathBuf {
        self.index_path.join(relative_path)
    }

    fn open_writable(&self, relative_path: &PathBuf) -> Result<File> {
        let full_path = self.resolve_path(relative_path);
        match File::create(full_path.clone()) {
            Ok(f) => Ok(f),
            Err(err) => {
                let path_str = full_path.to_str().unwrap_or("<error on to_str>");
                return Err(Error::IOError(err.kind(), String::from("Could not create file") + path_str))
            }
        }
    }

    fn mmap(&self, relative_path: &PathBuf) -> Result<MmapReadOnly> {
        let full_path = self.resolve_path(relative_path);
        let mut mmap_cache = self.mmap_cache.borrow_mut();
        if !mmap_cache.contains_key(&full_path) {
            mmap_cache.insert(full_path.clone(), try!(open_mmap(&full_path)) );
        }
        let mmap_readonly: &MmapReadOnly = mmap_cache.get(&full_path).unwrap();
        // // TODO remove if a proper clone is available
        let len = unsafe { mmap_readonly.as_slice().len() };
        Ok(mmap_readonly.range(0, len))
    }
}

/////////////////////////
// Segment

pub enum SegmentComponent {
    POSTINGS,
    // POSITIONS,
    TERMS,
}

#[derive(Debug, Clone)]
pub struct Segment {
    directory: Directory,
    segment_id: SegmentId,
}

impl Segment {
    fn path_suffix(component: &SegmentComponent)-> &'static str {
        match *component {
            SegmentComponent::POSTINGS => ".idx",
            // SegmentComponent::POSITIONS => ".pos",
            SegmentComponent::TERMS => ".term",
        }
    }

    pub fn relative_path(&self, component: &SegmentComponent) -> PathBuf {
        let SegmentId(ref segment_id_str) = self.segment_id;
        let filename = String::new() + segment_id_str + Segment::path_suffix(component);
        PathBuf::from(filename)
    }

    pub fn mmap(&self, component: SegmentComponent) -> Result<MmapReadOnly> {
        let path = self.relative_path(&component);
        self.directory.mmap(&path)
    }

    pub fn open_writable(&self, component: SegmentComponent) -> Result<File> {
        let path = self.relative_path(&component);
        self.directory.open_writable(&path)
    }
}
<commit_msg>removed useless asref<commit_after>
use std::path::{PathBuf, Path};
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::fs::File;
use std::io::Write;
use std::io::BufWriter;
use std::io;
use std::borrow::Borrow;
use std::borrow::BorrowMut;
use std::rc::Rc;
use std::sync::{Arc, Mutex, RwLock, MutexGuard, RwLockWriteGuard, RwLockReadGuard};
use std::fmt;
use std::ops::Deref;
use std::cell::RefCell;
use core::error::*;
use rand::{thread_rng, Rng};
use fst::raw::MmapReadOnly;
use rustc_serialize::json;
use atomicwrites;
use tempdir::TempDir;

#[derive(Clone, Debug)]
pub struct SegmentId(pub String);

pub fn generate_segment_name() -> SegmentId {
    static CHARS: &'static [u8] = b"abcdefghijklmnopqrstuvwxyz0123456789";
    let random_name: String = (0..8)
            .map(|_| thread_rng().choose(CHARS).unwrap().clone() as char)
            .collect();
    SegmentId( String::from("_") + &random_name)
}

// #[derive()]
#[derive(Clone,Debug,RustcDecodable, RustcEncodable)]
pub struct DirectoryMeta {
    segments: Vec<String>
}

impl DirectoryMeta {
    fn new() -> DirectoryMeta {
        DirectoryMeta {
            segments: Vec::new()
        }
    }
}

impl fmt::Debug for Directory {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
       write!(f, "Directory({:?})", self.inner_directory.read().unwrap().index_path)
   }
}

fn open_mmap(full_path: &PathBuf) -> Result<MmapReadOnly> {
    match MmapReadOnly::open_path(full_path.clone()) {
        Ok(mmapped_file) => Ok(mmapped_file),
        Err(ioerr) => {
            // TODO add file
            let error_msg = format!("Read-Only MMap of {:?} failed", full_path);
            return Err(Error::IOError(ioerr.kind(), error_msg));
        }
    }
}

fn sync_file(filepath: &PathBuf) -> Result<()> {
    match File::open(filepath.clone()) {
        Ok(fd) => {
            match fd.sync_all() {
                Err(err) => Err(Error::IOError(err.kind(), format!("Failed to sync {:?}", filepath))),
                _ => Ok(())
            }
        },
        Err(err) => Err(Error::IOError(err.kind(), format!("Cause: {:?}", err)))
    }
}

#[derive(Clone)]
pub struct Directory {
    inner_directory: Arc<RwLock<InnerDirectory>>,
}

impl Directory {

    fn get_write(&mut self) -> Result<RwLockWriteGuard<InnerDirectory>> {
        match self.inner_directory.write() {
            Ok(dir) =>
                Ok(dir),
            Err(e) =>
                Err(Error::LockError(format!("Could not acquire write lock on directory. {:?}", e)))
        }
    }

    fn get_read(&self) -> Result<RwLockReadGuard<InnerDirectory>> {
        match self.inner_directory.read() {
            Ok(dir) =>
                Ok(dir),
            Err(e) =>
                Err(Error::LockError(format!("Could not acquire read lock on directory. {:?}", e)))
        }
    }

    pub fn publish_segment(&mut self, segment: Segment) -> Result<()> {
        return try!(self.get_write()).publish_segment(segment);
    }

    pub fn open(filepath: &Path) -> Result<Directory> {
        let inner_directory = try!(InnerDirectory::open(filepath));
        Ok(Directory {
            inner_directory: Arc::new(RwLock::new(inner_directory)),
        })
    }

    pub fn from_tempdir() -> Result<Directory> {
        let inner_directory = try!(InnerDirectory::from_tempdir());
        Ok(Directory {
            inner_directory: Arc::new(RwLock::new(inner_directory)),
        })
    }

    pub fn load_metas(&self,) -> Result<()> {
        match self.inner_directory.read() {
            Ok(dir) => dir.load_metas(),
            Err(e) => Err(Error::LockError(format!("Could not get read lock {:?} for directory", e)))
        }
    }

    pub fn sync(&mut self, segment: Segment) -> Result<()> {
        try!(self.get_write()).sync(segment)
    }

    pub fn segments(&self,) -> Vec<Segment> {
        match self.inner_directory.read() {
            Ok(inner) => inner
                    .segment_ids()
                    .into_iter()
                    .map(|segment_id| self.segment(&segment_id))
                    .collect(),
            Err(e) => {
                //Err(Error::LockError(format!("Could not obtain read lock for {:?}", self)))
                // TODO make it return a result
                panic!("Could not work");
            }
        }

    }

    pub fn segment(&self, segment_id: &SegmentId) -> Segment {
        Segment {
            directory: self.clone(),
            segment_id: segment_id.clone()
        }
    }

    pub fn new_segment(&self,) -> Segment {
        // TODO check it does not exists
        self.segment(&generate_segment_name())
    }

    fn open_writable(&self, relative_path: &PathBuf) -> Result<File> {
        try!(self.get_read()).open_writable(relative_path)
    }

    fn mmap(&self, relative_path: &PathBuf) -> Result<MmapReadOnly> {
        try!(self.get_read()).mmap(relative_path)
    }
}

struct InnerDirectory {
    index_path: PathBuf,
    mmap_cache: RefCell<HashMap<PathBuf, MmapReadOnly>>,
    metas: DirectoryMeta,
    _temp_directory: Option<TempDir>,
}

fn create_tempdir() -> Result<TempDir> {
    let tempdir_res = TempDir::new("index");
    match tempdir_res {
        Ok(tempdir) => Ok(tempdir),
        Err(_) => Err(Error::FileNotFound(String::from("Could not create temp directory")))
    }
}

impl InnerDirectory {

    // TODO find a rusty way to hide that, while keeping
    // it visible for IndexWriters.
    pub fn publish_segment(&mut self, segment: Segment) -> Result<()> {
        self.metas.segments.push(segment.segment_id.0.clone());
        // TODO use logs
        self.save_metas()
    }

    pub fn open(filepath: &Path) -> Result<InnerDirectory> {
        let mut directory = InnerDirectory {
            index_path: PathBuf::from(filepath),
            mmap_cache: RefCell::new(HashMap::new()),
            metas: DirectoryMeta::new(),
            _temp_directory: None,
        };
        try!(directory.load_metas()); //< does the directory already exists?
        Ok(directory)
    }

    pub fn segment_ids(&self,) -> Vec<SegmentId> {
        self.metas
            .segments
            .iter()
            .cloned()
            .map(SegmentId)
            .collect()
    }

    pub fn from_tempdir() -> Result<InnerDirectory> {
        let tempdir = try!(create_tempdir());
        let tempdir_path = PathBuf::from(tempdir.path());
        let mut directory = InnerDirectory {
            index_path: PathBuf::from(tempdir_path),
            mmap_cache: RefCell::new(HashMap::new()),
            metas: DirectoryMeta::new(),
            _temp_directory: Some(tempdir)
        };
        //< does the directory already exists?
        try!(directory.load_metas());
        Ok(directory)
    }

    pub fn load_metas(&self,) -> Result<()> {
        // TODO load segment info
        Ok(())
    }

    fn meta_filepath(&self,) -> PathBuf {
        self.resolve_path(&PathBuf::from("meta.json"))
    }

    pub fn save_metas(&self,) -> Result<()> {
        let encoded = json::encode(&self.metas).unwrap();
        let meta_filepath = self.meta_filepath();
        let meta_file = atomicwrites::AtomicFile::new(meta_filepath, atomicwrites::AllowOverwrite);
        let write_result = meta_file.write(|f| {
            f.write_all(encoded.as_bytes())
        });
        match write_result {
            Ok(_) => { Ok(()) },
            Err(ioerr) => Err(Error::IOError(ioerr.kind(), format!("Failed to write meta file : {:?}", ioerr))),
        }
    }

    pub fn sync(&mut self, segment: Segment) -> Result<()> {
        for component in [SegmentComponent::POSTINGS, SegmentComponent::TERMS].iter() {
            let relative_path = segment.relative_path(component);
            let full_path = self.resolve_path(&relative_path);
            try!(sync_file(&full_path));
        }
        // syncing the directory itself
        try!(sync_file(&self.index_path));
        Ok(())
    }

    fn resolve_path(&self, relative_path: &PathBuf) -> PathBuf {
        self.index_path.join(relative_path)
    }

    fn open_writable(&self, relative_path: &PathBuf) -> Result<File> {
        let full_path = self.resolve_path(relative_path);
        match File::create(full_path.clone()) {
            Ok(f) => Ok(f),
            Err(err) => {
                let path_str = full_path.to_str().unwrap_or("<error on to_str>");
                return Err(Error::IOError(err.kind(), String::from("Could not create file") + path_str))
            }
        }
    }

    fn mmap(&self, relative_path: &PathBuf) -> Result<MmapReadOnly> {
        let full_path = self.resolve_path(relative_path);
        let mut mmap_cache = self.mmap_cache.borrow_mut();
        if !mmap_cache.contains_key(&full_path) {
            mmap_cache.insert(full_path.clone(), try!(open_mmap(&full_path)) );
        }
        let mmap_readonly: &MmapReadOnly = mmap_cache.get(&full_path).unwrap();
        // // TODO remove if a proper clone is available
        let len = unsafe { mmap_readonly.as_slice().len() };
        Ok(mmap_readonly.range(0, len))
    }
}

/////////////////////////
// Segment

pub enum SegmentComponent {
    POSTINGS,
    // POSITIONS,
    TERMS,
}

#[derive(Debug, Clone)]
pub struct Segment {
    directory: Directory,
    segment_id: SegmentId,
}

impl Segment {
    fn path_suffix(component: &SegmentComponent)-> &'static str {
        match *component {
            SegmentComponent::POSTINGS => ".idx",
            // SegmentComponent::POSITIONS => ".pos",
            SegmentComponent::TERMS => ".term",
        }
    }

    pub fn relative_path(&self, component: &SegmentComponent) -> PathBuf {
        let SegmentId(ref segment_id_str) = self.segment_id;
        let filename = String::new() + segment_id_str + Segment::path_suffix(component);
        PathBuf::from(filename)
    }

    pub fn mmap(&self, component: SegmentComponent) -> Result<MmapReadOnly> {
        let path = self.relative_path(&component);
        self.directory.mmap(&path)
    }

    pub fn open_writable(&self, component: SegmentComponent) -> Result<File> {
        let path = self.relative_path(&component);
        self.directory.open_writable(&path)
    }
}
<|endoftext|>