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

use crate::utils;
use serde::{Deserialize, Serialize};
use std::{
    net::{IpAddr, Ipv4Addr, SocketAddr},
    path::PathBuf,
};

pub const TARGET_SNAPSHOT_SIZE: usize = 100_000;

/// Port selected RocksDB options for tuning underlying rocksdb instance of AptosDB.
/// see <https://github.com/facebook/rocksdb/blob/master/include/rocksdb/options.h>
/// for detailed explanations.
#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct RocksdbConfig {
    pub max_open_files: i32,
    pub max_total_wal_size: u64,
    pub max_background_jobs: i32,
}

#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct RocksdbConfigs {
    pub ledger_db_config: RocksdbConfig,
    pub state_merkle_db_config: RocksdbConfig,
    pub index_db_config: RocksdbConfig,
}

impl Default for RocksdbConfigs {
    fn default() -> Self {
        Self {
            ledger_db_config: RocksdbConfig {
                // Allow db to close old sst files, saving memory.
                max_open_files: 5000,
                // For now we set the max total WAL size to be 1G. This config can be useful when column
                // families are updated at non-uniform frequencies.
                max_total_wal_size: 1u64 << 30,
                // This includes threads for flashing and compaction. Rocksdb will decide the # of
                // threads to use internally.
                max_background_jobs: 16,
            },
            state_merkle_db_config: RocksdbConfig {
                // Allow db to close old sst files, saving memory.
                max_open_files: 5000,
                // For now we set the max total WAL size to be 1G. This config can be useful when column
                // families are updated at non-uniform frequencies.
                max_total_wal_size: 1u64 << 30,
                // This includes threads for flashing and compaction. Rocksdb will decide the # of
                // threads to use internally.
                max_background_jobs: 16,
            },
            index_db_config: RocksdbConfig {
                // Allow db to close old sst files, saving memory.
                max_open_files: 1000,
                // For now we set the max total WAL size to be 1G. This config can be useful when column
                // families are updated at non-uniform frequencies.
                max_total_wal_size: 1u64 << 30,
                // This includes threads for flashing and compaction. Rocksdb will decide the # of
                // threads to use internally.
                max_background_jobs: 16,
            },
        }
    }
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct StorageConfig {
    pub address: SocketAddr,
    pub backup_service_address: SocketAddr,
    pub dir: PathBuf,
    pub grpc_max_receive_len: Option<i32>,
    pub storage_pruner_config: StoragePrunerConfig,
    #[serde(skip)]
    data_dir: PathBuf,
    /// Read, Write, Connect timeout for network operations in milliseconds
    pub timeout_ms: u64,
    /// The threshold that determine whether a snapshot should be committed to state merkle db.
    pub target_snapshot_size: usize,
    /// Rocksdb-specific configurations
    pub rocksdb_configs: RocksdbConfigs,
    /// Try to enable the internal indexer. The indexer expects to have seen all transactions
    /// since genesis. To recover operation after data loss, or to bootstrap a node in fast sync
    /// mode, the indexer db needs to be copied in from another node.
    pub enable_indexer: bool,
}

pub const NO_OP_STORAGE_PRUNER_CONFIG: StoragePrunerConfig = StoragePrunerConfig {
    enable_state_store_pruner: false,
    enable_ledger_pruner: false,
    state_store_prune_window: 0,
    ledger_prune_window: 0,
    ledger_pruning_batch_size: 10_000,
    state_store_pruning_batch_size: 10_000,
    user_pruning_window_offset: 0,
};

#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct StoragePrunerConfig {
    /// Boolean to enable/disable the state store pruner. The state pruner is responsible for
    /// pruning state tree nodes.
    pub enable_state_store_pruner: bool,
    /// Boolean to enable/disable the ledger pruner. The ledger pruner is responsible for pruning
    /// everything else except for states (e.g. transactions, events etc.)
    pub enable_ledger_pruner: bool,
    /// The size of the window should be calculated based on disk space availability and system TPS.
    pub state_store_prune_window: u64,
    /// This is the default pruning window for any other store except for state store. State store
    /// being big in size, we might want to configure a smaller window for state store vs other
    /// store.
    pub ledger_prune_window: u64,
    /// Batch size of the versions to be sent to the ledger pruner - this is to avoid slowdown due to
    /// issuing too many DB calls and batch prune instead. For ledger pruner, this means the number
    /// of versions to prune a time.
    pub ledger_pruning_batch_size: usize,
    /// Similar to the variable above but for state store pruner. It means the number of stale
    /// nodes to prune a time.
    pub state_store_pruning_batch_size: usize,
    /// The offset for user pruning window to adjust
    pub user_pruning_window_offset: u64,
}

impl Default for StoragePrunerConfig {
    fn default() -> Self {
        StoragePrunerConfig {
            enable_state_store_pruner: true,
            enable_ledger_pruner: true,
            state_store_prune_window: 1_000_000,
            ledger_prune_window: 10_000_000,
            ledger_pruning_batch_size: 500,
            // A 10k transaction block (touching 60k state values, in the case of the account
            // creation benchmark) on a 4B items DB (or 1.33B accounts) yields 300k JMT nodes
            state_store_pruning_batch_size: 1_000,
            user_pruning_window_offset: 200_000,
        }
    }
}

impl Default for StorageConfig {
    fn default() -> StorageConfig {
        StorageConfig {
            address: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 6666),
            backup_service_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 6186),
            dir: PathBuf::from("db"),
            grpc_max_receive_len: Some(100_000_000),
            // The prune window must at least out live a RPC request because its sub requests are
            // to return a consistent view of the DB at exactly same version. Considering a few
            // thousand TPS we are potentially going to achieve, and a few minutes a consistent view
            // of the DB might require, 10k (TPS)  * 100 (seconds)  =  1 Million might be a
            // conservatively safe minimal prune window. It'll take a few Gigabytes of disk space
            // depending on the size of an average account blob.
            storage_pruner_config: StoragePrunerConfig::default(),
            data_dir: PathBuf::from("/opt/aptos/data"),
            // Default read/write/connection timeout, in milliseconds
            timeout_ms: 30_000,
            rocksdb_configs: RocksdbConfigs::default(),
            enable_indexer: false,
            target_snapshot_size: TARGET_SNAPSHOT_SIZE,
        }
    }
}

impl StorageConfig {
    pub fn dir(&self) -> PathBuf {
        if self.dir.is_relative() {
            self.data_dir.join(&self.dir)
        } else {
            self.dir.clone()
        }
    }

    pub fn set_data_dir(&mut self, data_dir: PathBuf) {
        self.data_dir = data_dir;
    }

    pub fn randomize_ports(&mut self) {
        self.address.set_port(utils::get_available_port());
        self.backup_service_address
            .set_port(utils::get_available_port());
    }
}
<commit_msg>[storage] trivial: remove unused config<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::utils;
use serde::{Deserialize, Serialize};
use std::{
    net::{IpAddr, Ipv4Addr, SocketAddr},
    path::PathBuf,
};

pub const TARGET_SNAPSHOT_SIZE: usize = 100_000;

/// Port selected RocksDB options for tuning underlying rocksdb instance of AptosDB.
/// see <https://github.com/facebook/rocksdb/blob/master/include/rocksdb/options.h>
/// for detailed explanations.
#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct RocksdbConfig {
    pub max_open_files: i32,
    pub max_total_wal_size: u64,
    pub max_background_jobs: i32,
}

#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct RocksdbConfigs {
    pub ledger_db_config: RocksdbConfig,
    pub state_merkle_db_config: RocksdbConfig,
    pub index_db_config: RocksdbConfig,
}

impl Default for RocksdbConfigs {
    fn default() -> Self {
        Self {
            ledger_db_config: RocksdbConfig {
                // Allow db to close old sst files, saving memory.
                max_open_files: 5000,
                // For now we set the max total WAL size to be 1G. This config can be useful when column
                // families are updated at non-uniform frequencies.
                max_total_wal_size: 1u64 << 30,
                // This includes threads for flashing and compaction. Rocksdb will decide the # of
                // threads to use internally.
                max_background_jobs: 16,
            },
            state_merkle_db_config: RocksdbConfig {
                // Allow db to close old sst files, saving memory.
                max_open_files: 5000,
                // For now we set the max total WAL size to be 1G. This config can be useful when column
                // families are updated at non-uniform frequencies.
                max_total_wal_size: 1u64 << 30,
                // This includes threads for flashing and compaction. Rocksdb will decide the # of
                // threads to use internally.
                max_background_jobs: 16,
            },
            index_db_config: RocksdbConfig {
                // Allow db to close old sst files, saving memory.
                max_open_files: 1000,
                // For now we set the max total WAL size to be 1G. This config can be useful when column
                // families are updated at non-uniform frequencies.
                max_total_wal_size: 1u64 << 30,
                // This includes threads for flashing and compaction. Rocksdb will decide the # of
                // threads to use internally.
                max_background_jobs: 16,
            },
        }
    }
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct StorageConfig {
    pub address: SocketAddr,
    pub backup_service_address: SocketAddr,
    pub dir: PathBuf,
    pub storage_pruner_config: StoragePrunerConfig,
    #[serde(skip)]
    data_dir: PathBuf,
    /// The threshold that determine whether a snapshot should be committed to state merkle db.
    pub target_snapshot_size: usize,
    /// Rocksdb-specific configurations
    pub rocksdb_configs: RocksdbConfigs,
    /// Try to enable the internal indexer. The indexer expects to have seen all transactions
    /// since genesis. To recover operation after data loss, or to bootstrap a node in fast sync
    /// mode, the indexer db needs to be copied in from another node.
    pub enable_indexer: bool,
}

pub const NO_OP_STORAGE_PRUNER_CONFIG: StoragePrunerConfig = StoragePrunerConfig {
    enable_state_store_pruner: false,
    enable_ledger_pruner: false,
    state_store_prune_window: 0,
    ledger_prune_window: 0,
    ledger_pruning_batch_size: 10_000,
    state_store_pruning_batch_size: 10_000,
    user_pruning_window_offset: 0,
};

#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct StoragePrunerConfig {
    /// Boolean to enable/disable the state store pruner. The state pruner is responsible for
    /// pruning state tree nodes.
    pub enable_state_store_pruner: bool,
    /// Boolean to enable/disable the ledger pruner. The ledger pruner is responsible for pruning
    /// everything else except for states (e.g. transactions, events etc.)
    pub enable_ledger_pruner: bool,
    /// The size of the window should be calculated based on disk space availability and system TPS.
    pub state_store_prune_window: u64,
    /// This is the default pruning window for any other store except for state store. State store
    /// being big in size, we might want to configure a smaller window for state store vs other
    /// store.
    pub ledger_prune_window: u64,
    /// Batch size of the versions to be sent to the ledger pruner - this is to avoid slowdown due to
    /// issuing too many DB calls and batch prune instead. For ledger pruner, this means the number
    /// of versions to prune a time.
    pub ledger_pruning_batch_size: usize,
    /// Similar to the variable above but for state store pruner. It means the number of stale
    /// nodes to prune a time.
    pub state_store_pruning_batch_size: usize,
    /// The offset for user pruning window to adjust
    pub user_pruning_window_offset: u64,
}

impl Default for StoragePrunerConfig {
    fn default() -> Self {
        StoragePrunerConfig {
            enable_state_store_pruner: true,
            enable_ledger_pruner: true,
            state_store_prune_window: 1_000_000,
            ledger_prune_window: 10_000_000,
            ledger_pruning_batch_size: 500,
            // A 10k transaction block (touching 60k state values, in the case of the account
            // creation benchmark) on a 4B items DB (or 1.33B accounts) yields 300k JMT nodes
            state_store_pruning_batch_size: 1_000,
            user_pruning_window_offset: 200_000,
        }
    }
}

impl Default for StorageConfig {
    fn default() -> StorageConfig {
        StorageConfig {
            address: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 6666),
            backup_service_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 6186),
            dir: PathBuf::from("db"),
            // The prune window must at least out live a RPC request because its sub requests are
            // to return a consistent view of the DB at exactly same version. Considering a few
            // thousand TPS we are potentially going to achieve, and a few minutes a consistent view
            // of the DB might require, 10k (TPS)  * 100 (seconds)  =  1 Million might be a
            // conservatively safe minimal prune window. It'll take a few Gigabytes of disk space
            // depending on the size of an average account blob.
            storage_pruner_config: StoragePrunerConfig::default(),
            data_dir: PathBuf::from("/opt/aptos/data"),
            rocksdb_configs: RocksdbConfigs::default(),
            enable_indexer: false,
            target_snapshot_size: TARGET_SNAPSHOT_SIZE,
        }
    }
}

impl StorageConfig {
    pub fn dir(&self) -> PathBuf {
        if self.dir.is_relative() {
            self.data_dir.join(&self.dir)
        } else {
            self.dir.clone()
        }
    }

    pub fn set_data_dir(&mut self, data_dir: PathBuf) {
        self.data_dir = data_dir;
    }

    pub fn randomize_ports(&mut self) {
        self.address.set_port(utils::get_available_port());
        self.backup_service_address
            .set_port(utils::get_available_port());
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use glob::Pattern;
use x_lint::prelude::*;

#[derive(Clone, Copy, Debug)]
pub(super) struct EofNewline;

impl Linter for EofNewline {
    fn name(&self) -> &'static str {
        "eof-newline"
    }
}

impl ContentLinter for EofNewline {
    fn pre_run<'l>(&self, file_ctx: &FileContext<'l>) -> Result<RunStatus<'l>> {
        Ok(skip_whitespace_checks(file_ctx))
    }

    fn run<'l>(
        &self,
        ctx: &ContentContext<'l>,
        out: &mut LintFormatter<'l, '_>,
    ) -> Result<RunStatus<'l>> {
        let content = match ctx.content() {
            Some(text) => text,
            None => return Ok(RunStatus::Skipped(SkipReason::NonUtf8)),
        };
        if !content.ends_with('\n') {
            out.write(LintLevel::Error, "missing newline at EOF");
        }
        Ok(RunStatus::Executed)
    }
}

#[derive(Clone, Copy, Debug)]
pub(super) struct TrailingWhitespace;

impl Linter for TrailingWhitespace {
    fn name(&self) -> &'static str {
        "trailing-whitespace"
    }
}

impl ContentLinter for TrailingWhitespace {
    fn pre_run<'l>(&self, file_ctx: &FileContext<'l>) -> Result<RunStatus<'l>> {
        Ok(skip_whitespace_checks(file_ctx))
    }

    fn run<'l>(
        &self,
        ctx: &ContentContext<'l>,
        out: &mut LintFormatter<'l, '_>,
    ) -> Result<RunStatus<'l>> {
        let content = match ctx.content() {
            Some(text) => text,
            None => return Ok(RunStatus::Skipped(SkipReason::NonUtf8)),
        };

        for (ln, line) in content.lines().enumerate().map(|(ln, line)| (ln + 1, line)) {
            if line.trim_end() != line {
                out.write(
                    LintLevel::Error,
                    format!("trailing whitespace at line {}", ln),
                );
            }
        }

        if content
            .lines()
            .rev()
            .take_while(|line| line.is_empty())
            .count()
            > 0
        {
            out.write(LintLevel::Error, "trailing whitespace at EOF");
        }

        Ok(RunStatus::Executed)
    }
}

fn skip_whitespace_checks<'l>(file: &FileContext<'l>) -> RunStatus<'l> {
    // glob based opt outs
    let patterns = [".github/actions/*/dist/*"];

    if let Some(pattern) = patterns
        .iter()
        .find(|s| Pattern::new(s).unwrap().matches_path(file.file_path()))
    {
        return RunStatus::Skipped(SkipReason::GlobExemption(pattern));
    };

    // extension based opt outs
    #[allow(clippy::single_match)]
    match file.extension() {
        Some("exp") => {
            return RunStatus::Skipped(SkipReason::UnsupportedExtension(file.extension()))
        }
        Some("errmap") => {
            return RunStatus::Skipped(SkipReason::UnsupportedExtension(file.extension()))
        }
        _ => (),
    }

    RunStatus::Executed
}
<commit_msg>x: only check for newline at EOF if file isn't empty<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use glob::Pattern;
use x_lint::prelude::*;

#[derive(Clone, Copy, Debug)]
pub(super) struct EofNewline;

impl Linter for EofNewline {
    fn name(&self) -> &'static str {
        "eof-newline"
    }
}

impl ContentLinter for EofNewline {
    fn pre_run<'l>(&self, file_ctx: &FileContext<'l>) -> Result<RunStatus<'l>> {
        Ok(skip_whitespace_checks(file_ctx))
    }

    fn run<'l>(
        &self,
        ctx: &ContentContext<'l>,
        out: &mut LintFormatter<'l, '_>,
    ) -> Result<RunStatus<'l>> {
        let content = match ctx.content() {
            Some(text) => text,
            None => return Ok(RunStatus::Skipped(SkipReason::NonUtf8)),
        };
        if !content.is_empty() && !content.ends_with('\n') {
            out.write(LintLevel::Error, "missing newline at EOF");
        }
        Ok(RunStatus::Executed)
    }
}

#[derive(Clone, Copy, Debug)]
pub(super) struct TrailingWhitespace;

impl Linter for TrailingWhitespace {
    fn name(&self) -> &'static str {
        "trailing-whitespace"
    }
}

impl ContentLinter for TrailingWhitespace {
    fn pre_run<'l>(&self, file_ctx: &FileContext<'l>) -> Result<RunStatus<'l>> {
        Ok(skip_whitespace_checks(file_ctx))
    }

    fn run<'l>(
        &self,
        ctx: &ContentContext<'l>,
        out: &mut LintFormatter<'l, '_>,
    ) -> Result<RunStatus<'l>> {
        let content = match ctx.content() {
            Some(text) => text,
            None => return Ok(RunStatus::Skipped(SkipReason::NonUtf8)),
        };

        for (ln, line) in content.lines().enumerate().map(|(ln, line)| (ln + 1, line)) {
            if line.trim_end() != line {
                out.write(
                    LintLevel::Error,
                    format!("trailing whitespace at line {}", ln),
                );
            }
        }

        if content
            .lines()
            .rev()
            .take_while(|line| line.is_empty())
            .count()
            > 0
        {
            out.write(LintLevel::Error, "trailing whitespace at EOF");
        }

        Ok(RunStatus::Executed)
    }
}

fn skip_whitespace_checks<'l>(file: &FileContext<'l>) -> RunStatus<'l> {
    // glob based opt outs
    let patterns = [".github/actions/*/dist/*"];

    if let Some(pattern) = patterns
        .iter()
        .find(|s| Pattern::new(s).unwrap().matches_path(file.file_path()))
    {
        return RunStatus::Skipped(SkipReason::GlobExemption(pattern));
    };

    // extension based opt outs
    #[allow(clippy::single_match)]
    match file.extension() {
        Some("exp") => {
            return RunStatus::Skipped(SkipReason::UnsupportedExtension(file.extension()))
        }
        Some("errmap") => {
            return RunStatus::Skipped(SkipReason::UnsupportedExtension(file.extension()))
        }
        _ => (),
    }

    RunStatus::Executed
}
<|endoftext|>


<commit_before>use index::btree::cursor::RTCursor;
use index::btree::node::read_node;
use index::btree::node::NodeData;
use index::btree::node::NodeReadHandler;
use index::btree::DeletionSet;
use index::btree::NodeCellRef;
use index::EntryKey;
use index::Ordering;
use index::Slice;
use std::fmt::Debug;
use std::marker::PhantomData;

pub fn search_node<KS, PS>(
    node_ref: &NodeCellRef,
    key: &EntryKey,
    ordering: Ordering,
    deleted: &DeletionSet,
) -> RTCursor<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    debug!("searching for {:?}", key);
    read_node(node_ref, |node_handler: &NodeReadHandler<KS, PS>| {
        let node = &**node_handler;
        let gen_empty_cursor = || RTCursor {
            index: 0,
            ordering,
            page: None,
            marker: PhantomData,
            deleted: deleted.clone(),
            current: None,
        };
        if let Some(right_node) = node.key_at_right_node(key) {
            debug!("Search found a node at the right side");
            return search_node(right_node, key, ordering, deleted);
        }
        debug!("search node have keys {:?}", node.keys());
        let mut pos = node.search(key);
        match node {
            &NodeData::External(ref n) => {
                debug!(
                    "search in external for {:?}, len {}, ordering {:?}, content: {:?}",
                    key,
                    n.len,
                    ordering,
                    &n.keys.as_slice_immute()[..n.len]
                );
                if ordering == Ordering::Backward {
                    debug!("found cursor pos {} for backwards, will be corrected", pos);
                    if pos > 0 {
                        pos -= 1;
                    }
                    debug!("cursor pos have been corrected to {}", pos);
                }
                RTCursor::new(pos, node_ref, ordering, deleted)
            }
            &NodeData::Internal(ref n) => {
                debug!(
                    "search in internal node for {:?}, len {}, pos {}",
                    key, n.len, pos
                );
                let next_node_ref = &n.ptrs.as_slice_immute()[pos];
                n.debug_check_integrity();
                debug_assert!(pos <= n.len);
                debug_assert!(
                    !next_node_ref.is_default(),
                    "default node at pos {}, len {}, keys {:?}",
                    pos,
                    n.len,
                    &n.keys.as_slice_immute()[..pos]
                );
                search_node(next_node_ref, key, ordering, deleted)
            }
            &NodeData::Empty(ref n) => search_node(&n.right, key, ordering, deleted),
            &NodeData::None => gen_empty_cursor(),
        }
    })
}

pub enum MutSearchResult {
    External,
    Internal(NodeCellRef),
}

pub fn mut_search<KS, PS>(node_ref: &NodeCellRef, key: &EntryKey) -> MutSearchResult
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    read_node(node_ref, |node: &NodeReadHandler<KS, PS>| match &**node {
        &NodeData::Internal(ref n) => {
            let pos = n.search(key);
            let sub_node = n.ptrs.as_slice_immute()[pos].clone();
            MutSearchResult::Internal(sub_node)
        }
        &NodeData::External(_) => MutSearchResult::External,
        &NodeData::Empty(ref n) => mut_search::<KS, PS>(&n.right, key),
        &NodeData::None => unreachable!(),
    })
}
<commit_msg>will not check integrity when latch have not been obtained<commit_after>use index::btree::cursor::RTCursor;
use index::btree::node::read_node;
use index::btree::node::NodeData;
use index::btree::node::NodeReadHandler;
use index::btree::DeletionSet;
use index::btree::NodeCellRef;
use index::EntryKey;
use index::Ordering;
use index::Slice;
use std::fmt::Debug;
use std::marker::PhantomData;

pub fn search_node<KS, PS>(
    node_ref: &NodeCellRef,
    key: &EntryKey,
    ordering: Ordering,
    deleted: &DeletionSet,
) -> RTCursor<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    debug!("searching for {:?}", key);
    read_node(node_ref, |node_handler: &NodeReadHandler<KS, PS>| {
        let node = &**node_handler;
        let gen_empty_cursor = || RTCursor {
            index: 0,
            ordering,
            page: None,
            marker: PhantomData,
            deleted: deleted.clone(),
            current: None,
        };
        if let Some(right_node) = node.key_at_right_node(key) {
            debug!("Search found a node at the right side");
            return search_node(right_node, key, ordering, deleted);
        }
        debug!("search node have keys {:?}", node.keys());
        let mut pos = node.search(key);
        match node {
            &NodeData::External(ref n) => {
                debug!(
                    "search in external for {:?}, len {}, ordering {:?}, content: {:?}",
                    key,
                    n.len,
                    ordering,
                    &n.keys.as_slice_immute()[..n.len]
                );
                if ordering == Ordering::Backward {
                    debug!("found cursor pos {} for backwards, will be corrected", pos);
                    if pos > 0 {
                        pos -= 1;
                    }
                    debug!("cursor pos have been corrected to {}", pos);
                }
                RTCursor::new(pos, node_ref, ordering, deleted)
            }
            &NodeData::Internal(ref n) => {
                debug!(
                    "search in internal node for {:?}, len {}, pos {}",
                    key, n.len, pos
                );
                let next_node_ref = &n.ptrs.as_slice_immute()[pos];
                debug_assert!(pos <= n.len);
                debug_assert!(
                    !next_node_ref.is_default(),
                    "default node at pos {}, len {}, keys {:?}",
                    pos,
                    n.len,
                    &n.keys.as_slice_immute()[..pos]
                );
                search_node(next_node_ref, key, ordering, deleted)
            }
            &NodeData::Empty(ref n) => search_node(&n.right, key, ordering, deleted),
            &NodeData::None => gen_empty_cursor(),
        }
    })
}

pub enum MutSearchResult {
    External,
    Internal(NodeCellRef),
}

pub fn mut_search<KS, PS>(node_ref: &NodeCellRef, key: &EntryKey) -> MutSearchResult
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    read_node(node_ref, |node: &NodeReadHandler<KS, PS>| match &**node {
        &NodeData::Internal(ref n) => {
            let pos = n.search(key);
            let sub_node = n.ptrs.as_slice_immute()[pos].clone();
            MutSearchResult::Internal(sub_node)
        }
        &NodeData::External(_) => MutSearchResult::External,
        &NodeData::Empty(ref n) => mut_search::<KS, PS>(&n.right, key),
        &NodeData::None => unreachable!(),
    })
}
<|endoftext|>


<commit_before>// Copyright 2016 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

#![allow(dead_code)]

extern crate combine;
#[macro_use]
extern crate error_chain;

extern crate edn;
extern crate mentat_tx;

#[macro_use]
extern crate mentat_parser_utils;

use combine::{
    eof,
    many,
    parser,
    satisfy,
    satisfy_map,
    Parser,
    ParseResult,
};
use mentat_tx::entities::{
    AtomOrLookupRefOrVectorOrMapNotation,
    Entid,
    EntidOrLookupRefOrTempId,
    Entity,
    LookupRef,
    MapNotation,
    OpType,
    TempId,
};
use mentat_parser_utils::{ResultParser};
use mentat_parser_utils::value_and_span::{
    Item,
    OfExactlyParsing,
    integer,
    list,
    map,
    namespaced_keyword,
    vector,
};

pub mod errors;
pub use errors::*;

pub struct Tx<'a>(std::marker::PhantomData<&'a ()>);

def_parser!(Tx, entid, Entid, {
    integer()
        .map(|x| Entid::Entid(x))
        .or(namespaced_keyword().map(|x| Entid::Ident(x.clone())))
});

def_matches_plain_symbol!(Tx, literal_lookup_ref, "lookup-ref");

def_parser!(Tx, lookup_ref, LookupRef, {
    list().of_exactly(
        Tx::literal_lookup_ref()
            .with((Tx::entid(),
                   Tx::atom()))
            .map(|(a, v)| LookupRef { a: a, v: v.clone().without_spans() }))
});

def_parser!(Tx, entid_or_lookup_ref_or_temp_id, EntidOrLookupRefOrTempId, {
    Tx::entid().map(EntidOrLookupRefOrTempId::Entid)
        .or(Tx::lookup_ref().map(EntidOrLookupRefOrTempId::LookupRef))
        .or(Tx::temp_id().map(EntidOrLookupRefOrTempId::TempId))
});

def_parser!(Tx, temp_id, TempId, {
    satisfy_map(|x: &'a edn::ValueAndSpan| x.as_text().cloned().map(TempId::External))
});

def_parser!(Tx, atom, &'a edn::ValueAndSpan, {
    satisfy_map(|x: &'a edn::ValueAndSpan| x.as_atom())
});

def_parser!(Tx, nested_vector, Vec<AtomOrLookupRefOrVectorOrMapNotation>, {
    vector().of_exactly(many(Tx::atom_or_lookup_ref_or_vector()))
});

def_parser!(Tx, atom_or_lookup_ref_or_vector, AtomOrLookupRefOrVectorOrMapNotation, {
    Tx::lookup_ref().map(AtomOrLookupRefOrVectorOrMapNotation::LookupRef)
        .or(Tx::nested_vector().map(AtomOrLookupRefOrVectorOrMapNotation::Vector))
        .or(Tx::map_notation().map(AtomOrLookupRefOrVectorOrMapNotation::MapNotation))
        .or(Tx::atom().map(|x| x.clone()).map(AtomOrLookupRefOrVectorOrMapNotation::Atom))
});

def_matches_namespaced_keyword!(Tx, literal_db_add, "db", "add");

def_matches_namespaced_keyword!(Tx, literal_db_retract, "db", "retract");

def_parser!(Tx, add_or_retract, Entity, {
    vector().of_exactly(
        (Tx::literal_db_add().map(|_| OpType::Add).or(Tx::literal_db_retract().map(|_| OpType::Retract)),
             Tx::entid_or_lookup_ref_or_temp_id(),
             Tx::entid(),
             Tx::atom_or_lookup_ref_or_vector())
        .map(|(op, e, a, v)| {
            Entity::AddOrRetract {
                op: op,
                e: e,
                a: a,
                v: v,
            }
        }))
});

def_parser!(Tx, map_notation, MapNotation, {
    map()
        .of_exactly(many((Tx::entid(), Tx::atom_or_lookup_ref_or_vector())))
        .map(|avs: Vec<(Entid, AtomOrLookupRefOrVectorOrMapNotation)>| -> MapNotation {
            avs.into_iter().collect()
        })
});

def_parser!(Tx, entity, Entity, {
    Tx::add_or_retract()
        .or(Tx::map_notation().map(Entity::MapNotation))
});

def_parser!(Tx, entities, Vec<Entity>, {
    vector().of_exactly(many(Tx::entity()))
});

impl<'a> Tx<'a> {
    pub fn parse(input: &'a edn::ValueAndSpan) -> std::result::Result<Vec<Entity>, errors::Error> {
        Tx::entities()
            .skip(eof())
            .parse(input.atom_stream())
            .map(|x| x.0)
            .map_err(|e| Error::from_kind(ErrorKind::ParseError(e.into())))
    }

    fn parse_entid_or_lookup_ref_or_temp_id(input: edn::ValueAndSpan) -> std::result::Result<EntidOrLookupRefOrTempId, errors::Error> {
        Tx::entid_or_lookup_ref_or_temp_id()
            .skip(eof())
            .parse(input.atom_stream())
            .map(|x| x.0)
            .map_err(|e| Error::from_kind(ErrorKind::ParseError(e.into())))
    }
}

/// Remove any :db/id value from the given map notation, converting the returned value into
/// something suitable for the entity position rather than something suitable for a value position.
///
/// This is here simply to not expose some of the internal APIs of the tx-parser.
pub fn remove_db_id(map: &mut MapNotation) -> std::result::Result<Option<EntidOrLookupRefOrTempId>, errors::Error> {
    // TODO: extract lazy defined constant.
    let db_id_key = Entid::Ident(edn::NamespacedKeyword::new("db", "id"));

    let db_id: Option<EntidOrLookupRefOrTempId> = if let Some(id) = map.remove(&db_id_key) {
        match id {
            AtomOrLookupRefOrVectorOrMapNotation::Atom(v) => {
                let db_id = Tx::parse_entid_or_lookup_ref_or_temp_id(v)
                    .chain_err(|| Error::from(ErrorKind::DbIdError))?;
                Some(db_id)
            },
            AtomOrLookupRefOrVectorOrMapNotation::LookupRef(_) |
            AtomOrLookupRefOrVectorOrMapNotation::Vector(_) |
            AtomOrLookupRefOrVectorOrMapNotation::MapNotation(_) => {
                bail!(ErrorKind::DbIdError)
            },
        }
    } else {
        None
    };

    Ok(db_id)
}

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

    use std::collections::BTreeMap;

    use combine::Parser;
    use edn::{
        NamespacedKeyword,
        PlainSymbol,
        Span,
        SpannedValue,
        Value,
        ValueAndSpan,
    };
    use mentat_tx::entities::{
        Entid,
        EntidOrLookupRefOrTempId,
        Entity,
        OpType,
        AtomOrLookupRefOrVectorOrMapNotation,
    };

    fn kw(namespace: &str, name: &str) -> Value {
        Value::NamespacedKeyword(NamespacedKeyword::new(namespace, name))
    }

    #[test]
    fn test_add() {
        let input = Value::Vector(vec![kw("db", "add"),
                                       kw("test", "entid"),
                                       kw("test", "a"),
                                       Value::Text("v".into())]);

        let input = input.with_spans();
        let stream = input.atom_stream();
        let result = Tx::entity().parse(stream).map(|x| x.0);

        assert_eq!(result,
                   Ok(Entity::AddOrRetract {
                       op: OpType::Add,
                       e: EntidOrLookupRefOrTempId::Entid(Entid::Ident(NamespacedKeyword::new("test",
                                                                                "entid"))),
                       a: Entid::Ident(NamespacedKeyword::new("test", "a")),
                       v: AtomOrLookupRefOrVectorOrMapNotation::Atom(ValueAndSpan::new(SpannedValue::Text("v".into()), Span(29, 32))),
                   }));
    }

    #[test]
    fn test_retract() {
        let input = Value::Vector(vec![kw("db", "retract"),
                                       Value::Integer(101),
                                       kw("test", "a"),
                                       Value::Text("v".into())]);

        let input = input.with_spans();
        let stream = input.atom_stream();
        let result = Tx::entity().parse(stream).map(|x| x.0);

        assert_eq!(result,
                   Ok(Entity::AddOrRetract {
                       op: OpType::Retract,
                       e: EntidOrLookupRefOrTempId::Entid(Entid::Entid(101)),
                       a: Entid::Ident(NamespacedKeyword::new("test", "a")),
                       v: AtomOrLookupRefOrVectorOrMapNotation::Atom(ValueAndSpan::new(SpannedValue::Text("v".into()), Span(25, 28))),
                   }));
    }

    #[test]
    fn test_lookup_ref() {
        let input = Value::Vector(vec![kw("db", "add"),
                                       Value::List(vec![Value::PlainSymbol(PlainSymbol::new("lookup-ref")),
                                                        kw("test", "a1"),
                                                        Value::Text("v1".into())].into_iter().collect()),
                                       kw("test", "a"),
                                       Value::Text("v".into())]);

        let input = input.with_spans();
        let stream = input.atom_stream();
        let result = Tx::entity().parse(stream).map(|x| x.0);

        assert_eq!(result,
                   Ok(Entity::AddOrRetract {
                       op: OpType::Add,
                       e: EntidOrLookupRefOrTempId::LookupRef(LookupRef {
                           a: Entid::Ident(NamespacedKeyword::new("test", "a1")),
                           v: Value::Text("v1".into()),
                       }),
                       a: Entid::Ident(NamespacedKeyword::new("test", "a")),
                       v: AtomOrLookupRefOrVectorOrMapNotation::Atom(ValueAndSpan::new(SpannedValue::Text("v".into()), Span(44, 47))),
                   }));
    }

    #[test]
    fn test_nested_vector() {
        let input = Value::Vector(vec![kw("db", "add"),
                                       Value::List(vec![Value::PlainSymbol(PlainSymbol::new("lookup-ref")),
                                                        kw("test", "a1"),
                                                        Value::Text("v1".into())].into_iter().collect()),
                                       kw("test", "a"),
                                       Value::Vector(vec![Value::Text("v1".into()), Value::Text("v2".into())])]);

        let input = input.with_spans();
        let stream = input.atom_stream();
        let result = Tx::entity().parse(stream).map(|x| x.0);

        assert_eq!(result,
                   Ok(Entity::AddOrRetract {
                       op: OpType::Add,
                       e: EntidOrLookupRefOrTempId::LookupRef(LookupRef {
                           a: Entid::Ident(NamespacedKeyword::new("test", "a1")),
                           v: Value::Text("v1".into()),
                       }),
                       a: Entid::Ident(NamespacedKeyword::new("test", "a")),
                       v: AtomOrLookupRefOrVectorOrMapNotation::Vector(vec![AtomOrLookupRefOrVectorOrMapNotation::Atom(ValueAndSpan::new(SpannedValue::Text("v1".into()), Span(45, 49))),
                                                                            AtomOrLookupRefOrVectorOrMapNotation::Atom(ValueAndSpan::new(SpannedValue::Text("v2".into()), Span(50, 54)))]),
                   }));
    }

    #[test]
    fn test_map_notation() {
        let mut expected: MapNotation = BTreeMap::default();
        expected.insert(Entid::Ident(NamespacedKeyword::new("db", "id")), AtomOrLookupRefOrVectorOrMapNotation::Atom(ValueAndSpan::new(SpannedValue::Text("t".to_string()), Span(8, 11))));
        expected.insert(Entid::Ident(NamespacedKeyword::new("db", "ident")), AtomOrLookupRefOrVectorOrMapNotation::Atom(ValueAndSpan::new(SpannedValue::NamespacedKeyword(NamespacedKeyword::new("test", "attribute")), Span(22, 37))));

        let mut map: BTreeMap<Value, Value> = BTreeMap::default();
        map.insert(kw("db", "id"), Value::Text("t".to_string()));
        map.insert(kw("db", "ident"), kw("test", "attribute"));
        let input = Value::Map(map.clone());

        let input = input.with_spans();
        let stream = input.atom_stream();
        let result = Tx::entity().parse(stream).map(|x| x.0);

        assert_eq!(result,
                   Ok(Entity::MapNotation(expected)));
    }
}
<commit_msg>Post: Use choice instead of or.<commit_after>// Copyright 2016 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

#![allow(dead_code)]

extern crate combine;
#[macro_use]
extern crate error_chain;

extern crate edn;
extern crate mentat_tx;

#[macro_use]
extern crate mentat_parser_utils;

use combine::{
    choice,
    eof,
    many,
    parser,
    satisfy,
    satisfy_map,
    try,
    Parser,
    ParseResult,
};
use mentat_tx::entities::{
    AtomOrLookupRefOrVectorOrMapNotation,
    Entid,
    EntidOrLookupRefOrTempId,
    Entity,
    LookupRef,
    MapNotation,
    OpType,
    TempId,
};
use mentat_parser_utils::{ResultParser};
use mentat_parser_utils::value_and_span::{
    Item,
    OfExactlyParsing,
    integer,
    list,
    map,
    namespaced_keyword,
    vector,
};

pub mod errors;
pub use errors::*;

pub struct Tx<'a>(std::marker::PhantomData<&'a ()>);

def_parser!(Tx, entid, Entid, {
    integer()
        .map(|x| Entid::Entid(x))
        .or(namespaced_keyword().map(|x| Entid::Ident(x.clone())))
});

def_matches_plain_symbol!(Tx, literal_lookup_ref, "lookup-ref");

def_parser!(Tx, lookup_ref, LookupRef, {
    list().of_exactly(
        Tx::literal_lookup_ref()
            .with((Tx::entid(),
                   Tx::atom()))
            .map(|(a, v)| LookupRef { a: a, v: v.clone().without_spans() }))
});

def_parser!(Tx, entid_or_lookup_ref_or_temp_id, EntidOrLookupRefOrTempId, {
    Tx::entid().map(EntidOrLookupRefOrTempId::Entid)
        .or(Tx::lookup_ref().map(EntidOrLookupRefOrTempId::LookupRef))
        .or(Tx::temp_id().map(EntidOrLookupRefOrTempId::TempId))
});

def_parser!(Tx, temp_id, TempId, {
    satisfy_map(|x: &'a edn::ValueAndSpan| x.as_text().cloned().map(TempId::External))
});

def_parser!(Tx, atom, &'a edn::ValueAndSpan, {
    satisfy_map(|x: &'a edn::ValueAndSpan| x.as_atom())
});

def_parser!(Tx, nested_vector, Vec<AtomOrLookupRefOrVectorOrMapNotation>, {
    vector().of_exactly(many(Tx::atom_or_lookup_ref_or_vector()))
});

def_parser!(Tx, atom_or_lookup_ref_or_vector, AtomOrLookupRefOrVectorOrMapNotation, {
    choice::<[&mut Parser<Input = _, Output = AtomOrLookupRefOrVectorOrMapNotation>; 4], _>
        ([&mut try(Tx::lookup_ref().map(AtomOrLookupRefOrVectorOrMapNotation::LookupRef)),
          &mut Tx::nested_vector().map(AtomOrLookupRefOrVectorOrMapNotation::Vector),
          &mut Tx::map_notation().map(AtomOrLookupRefOrVectorOrMapNotation::MapNotation),
          &mut Tx::atom().map(|x| x.clone()).map(AtomOrLookupRefOrVectorOrMapNotation::Atom)
        ])
});

def_matches_namespaced_keyword!(Tx, literal_db_add, "db", "add");

def_matches_namespaced_keyword!(Tx, literal_db_retract, "db", "retract");

def_parser!(Tx, add_or_retract, Entity, {
    vector().of_exactly(
        (Tx::literal_db_add().map(|_| OpType::Add).or(Tx::literal_db_retract().map(|_| OpType::Retract)),
             Tx::entid_or_lookup_ref_or_temp_id(),
             Tx::entid(),
             Tx::atom_or_lookup_ref_or_vector())
        .map(|(op, e, a, v)| {
            Entity::AddOrRetract {
                op: op,
                e: e,
                a: a,
                v: v,
            }
        }))
});

def_parser!(Tx, map_notation, MapNotation, {
    map()
        .of_exactly(many((Tx::entid(), Tx::atom_or_lookup_ref_or_vector())))
        .map(|avs: Vec<(Entid, AtomOrLookupRefOrVectorOrMapNotation)>| -> MapNotation {
            avs.into_iter().collect()
        })
});

def_parser!(Tx, entity, Entity, {
    Tx::add_or_retract()
        .or(Tx::map_notation().map(Entity::MapNotation))
});

def_parser!(Tx, entities, Vec<Entity>, {
    vector().of_exactly(many(Tx::entity()))
});

impl<'a> Tx<'a> {
    pub fn parse(input: &'a edn::ValueAndSpan) -> std::result::Result<Vec<Entity>, errors::Error> {
        Tx::entities()
            .skip(eof())
            .parse(input.atom_stream())
            .map(|x| x.0)
            .map_err(|e| Error::from_kind(ErrorKind::ParseError(e.into())))
    }

    fn parse_entid_or_lookup_ref_or_temp_id(input: edn::ValueAndSpan) -> std::result::Result<EntidOrLookupRefOrTempId, errors::Error> {
        Tx::entid_or_lookup_ref_or_temp_id()
            .skip(eof())
            .parse(input.atom_stream())
            .map(|x| x.0)
            .map_err(|e| Error::from_kind(ErrorKind::ParseError(e.into())))
    }
}

/// Remove any :db/id value from the given map notation, converting the returned value into
/// something suitable for the entity position rather than something suitable for a value position.
///
/// This is here simply to not expose some of the internal APIs of the tx-parser.
pub fn remove_db_id(map: &mut MapNotation) -> std::result::Result<Option<EntidOrLookupRefOrTempId>, errors::Error> {
    // TODO: extract lazy defined constant.
    let db_id_key = Entid::Ident(edn::NamespacedKeyword::new("db", "id"));

    let db_id: Option<EntidOrLookupRefOrTempId> = if let Some(id) = map.remove(&db_id_key) {
        match id {
            AtomOrLookupRefOrVectorOrMapNotation::Atom(v) => {
                let db_id = Tx::parse_entid_or_lookup_ref_or_temp_id(v)
                    .chain_err(|| Error::from(ErrorKind::DbIdError))?;
                Some(db_id)
            },
            AtomOrLookupRefOrVectorOrMapNotation::LookupRef(_) |
            AtomOrLookupRefOrVectorOrMapNotation::Vector(_) |
            AtomOrLookupRefOrVectorOrMapNotation::MapNotation(_) => {
                bail!(ErrorKind::DbIdError)
            },
        }
    } else {
        None
    };

    Ok(db_id)
}

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

    use std::collections::BTreeMap;

    use combine::Parser;
    use edn::{
        NamespacedKeyword,
        PlainSymbol,
        Span,
        SpannedValue,
        Value,
        ValueAndSpan,
    };
    use mentat_tx::entities::{
        Entid,
        EntidOrLookupRefOrTempId,
        Entity,
        OpType,
        AtomOrLookupRefOrVectorOrMapNotation,
    };

    fn kw(namespace: &str, name: &str) -> Value {
        Value::NamespacedKeyword(NamespacedKeyword::new(namespace, name))
    }

    #[test]
    fn test_add() {
        let input = Value::Vector(vec![kw("db", "add"),
                                       kw("test", "entid"),
                                       kw("test", "a"),
                                       Value::Text("v".into())]);

        let input = input.with_spans();
        let stream = input.atom_stream();
        let result = Tx::entity().parse(stream).map(|x| x.0);

        assert_eq!(result,
                   Ok(Entity::AddOrRetract {
                       op: OpType::Add,
                       e: EntidOrLookupRefOrTempId::Entid(Entid::Ident(NamespacedKeyword::new("test",
                                                                                "entid"))),
                       a: Entid::Ident(NamespacedKeyword::new("test", "a")),
                       v: AtomOrLookupRefOrVectorOrMapNotation::Atom(ValueAndSpan::new(SpannedValue::Text("v".into()), Span(29, 32))),
                   }));
    }

    #[test]
    fn test_retract() {
        let input = Value::Vector(vec![kw("db", "retract"),
                                       Value::Integer(101),
                                       kw("test", "a"),
                                       Value::Text("v".into())]);

        let input = input.with_spans();
        let stream = input.atom_stream();
        let result = Tx::entity().parse(stream).map(|x| x.0);

        assert_eq!(result,
                   Ok(Entity::AddOrRetract {
                       op: OpType::Retract,
                       e: EntidOrLookupRefOrTempId::Entid(Entid::Entid(101)),
                       a: Entid::Ident(NamespacedKeyword::new("test", "a")),
                       v: AtomOrLookupRefOrVectorOrMapNotation::Atom(ValueAndSpan::new(SpannedValue::Text("v".into()), Span(25, 28))),
                   }));
    }

    #[test]
    fn test_lookup_ref() {
        let input = Value::Vector(vec![kw("db", "add"),
                                       Value::List(vec![Value::PlainSymbol(PlainSymbol::new("lookup-ref")),
                                                        kw("test", "a1"),
                                                        Value::Text("v1".into())].into_iter().collect()),
                                       kw("test", "a"),
                                       Value::Text("v".into())]);

        let input = input.with_spans();
        let stream = input.atom_stream();
        let result = Tx::entity().parse(stream).map(|x| x.0);

        assert_eq!(result,
                   Ok(Entity::AddOrRetract {
                       op: OpType::Add,
                       e: EntidOrLookupRefOrTempId::LookupRef(LookupRef {
                           a: Entid::Ident(NamespacedKeyword::new("test", "a1")),
                           v: Value::Text("v1".into()),
                       }),
                       a: Entid::Ident(NamespacedKeyword::new("test", "a")),
                       v: AtomOrLookupRefOrVectorOrMapNotation::Atom(ValueAndSpan::new(SpannedValue::Text("v".into()), Span(44, 47))),
                   }));
    }

    #[test]
    fn test_nested_vector() {
        let input = Value::Vector(vec![kw("db", "add"),
                                       Value::List(vec![Value::PlainSymbol(PlainSymbol::new("lookup-ref")),
                                                        kw("test", "a1"),
                                                        Value::Text("v1".into())].into_iter().collect()),
                                       kw("test", "a"),
                                       Value::Vector(vec![Value::Text("v1".into()), Value::Text("v2".into())])]);

        let input = input.with_spans();
        let stream = input.atom_stream();
        let result = Tx::entity().parse(stream).map(|x| x.0);

        assert_eq!(result,
                   Ok(Entity::AddOrRetract {
                       op: OpType::Add,
                       e: EntidOrLookupRefOrTempId::LookupRef(LookupRef {
                           a: Entid::Ident(NamespacedKeyword::new("test", "a1")),
                           v: Value::Text("v1".into()),
                       }),
                       a: Entid::Ident(NamespacedKeyword::new("test", "a")),
                       v: AtomOrLookupRefOrVectorOrMapNotation::Vector(vec![AtomOrLookupRefOrVectorOrMapNotation::Atom(ValueAndSpan::new(SpannedValue::Text("v1".into()), Span(45, 49))),
                                                                            AtomOrLookupRefOrVectorOrMapNotation::Atom(ValueAndSpan::new(SpannedValue::Text("v2".into()), Span(50, 54)))]),
                   }));
    }

    #[test]
    fn test_map_notation() {
        let mut expected: MapNotation = BTreeMap::default();
        expected.insert(Entid::Ident(NamespacedKeyword::new("db", "id")), AtomOrLookupRefOrVectorOrMapNotation::Atom(ValueAndSpan::new(SpannedValue::Text("t".to_string()), Span(8, 11))));
        expected.insert(Entid::Ident(NamespacedKeyword::new("db", "ident")), AtomOrLookupRefOrVectorOrMapNotation::Atom(ValueAndSpan::new(SpannedValue::NamespacedKeyword(NamespacedKeyword::new("test", "attribute")), Span(22, 37))));

        let mut map: BTreeMap<Value, Value> = BTreeMap::default();
        map.insert(kw("db", "id"), Value::Text("t".to_string()));
        map.insert(kw("db", "ident"), kw("test", "attribute"));
        let input = Value::Map(map.clone());

        let input = input.with_spans();
        let stream = input.atom_stream();
        let result = Tx::entity().parse(stream).map(|x| x.0);

        assert_eq!(result,
                   Ok(Entity::MapNotation(expected)));
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use fnv::FnvHasher;
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use tiling::AuxiliaryListsMap;
use webrender_traits::{AuxiliaryLists, BuiltDisplayList, PipelineId, Epoch, ColorF};
use webrender_traits::{DisplayItem, SpecificDisplayItem, StackingContext};
use webrender_traits::LayerSize;

trait DisplayListHelpers {
    fn starting_stacking_context<'a>(&'a self) -> Option<&'a StackingContext>;
}

impl DisplayListHelpers for Vec<DisplayItem> {
    fn starting_stacking_context<'a>(&'a self) -> Option<&'a StackingContext> {
        self.first().and_then(|item| match item.item {
            SpecificDisplayItem::PushStackingContext(ref item) => Some(&item.stacking_context),
            _ => None,
        })
    }
}

/// A representation of the layout within the display port for a given document or iframe.
#[derive(Debug)]
pub struct ScenePipeline {
    pub pipeline_id: PipelineId,
    pub epoch: Epoch,
    pub viewport_size: LayerSize,
    pub background_color: Option<ColorF>,
}

/// A complete representation of the layout bundling visible pipelines together.
pub struct Scene {
    pub root_pipeline_id: Option<PipelineId>,
    pub pipeline_map: HashMap<PipelineId, ScenePipeline, BuildHasherDefault<FnvHasher>>,
    pub pipeline_sizes: HashMap<PipelineId, LayerSize>,
    pub pipeline_auxiliary_lists: AuxiliaryListsMap,
    pub display_lists: HashMap<PipelineId, Vec<DisplayItem>, BuildHasherDefault<FnvHasher>>,
}

impl Scene {
    pub fn new() -> Scene {
        Scene {
            root_pipeline_id: None,
            pipeline_sizes: HashMap::new(),
            pipeline_map: HashMap::with_hasher(Default::default()),
            pipeline_auxiliary_lists: HashMap::with_hasher(Default::default()),
            display_lists: HashMap::with_hasher(Default::default()),
        }
    }

    pub fn set_root_pipeline_id(&mut self, pipeline_id: PipelineId) {
        self.root_pipeline_id = Some(pipeline_id);
    }

    pub fn set_root_display_list(&mut self,
                                 pipeline_id: PipelineId,
                                 epoch: Epoch,
                                 built_display_list: BuiltDisplayList,
                                 background_color: Option<ColorF>,
                                 viewport_size: LayerSize,
                                 auxiliary_lists: AuxiliaryLists) {
        self.pipeline_auxiliary_lists.insert(pipeline_id, auxiliary_lists);
        self.display_lists.insert(pipeline_id, built_display_list.all_display_items().to_vec());

        let new_pipeline = ScenePipeline {
            pipeline_id: pipeline_id,
            epoch: epoch,
            viewport_size: viewport_size,
            background_color: background_color,
        };

        self.pipeline_map.insert(pipeline_id, new_pipeline);
    }
}
<commit_msg>Remove unused trait<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use fnv::FnvHasher;
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use tiling::AuxiliaryListsMap;
use webrender_traits::{AuxiliaryLists, BuiltDisplayList, PipelineId, Epoch, ColorF};
use webrender_traits::{DisplayItem, SpecificDisplayItem, StackingContext};
use webrender_traits::LayerSize;

/// A representation of the layout within the display port for a given document or iframe.
#[derive(Debug)]
pub struct ScenePipeline {
    pub pipeline_id: PipelineId,
    pub epoch: Epoch,
    pub viewport_size: LayerSize,
    pub background_color: Option<ColorF>,
}

/// A complete representation of the layout bundling visible pipelines together.
pub struct Scene {
    pub root_pipeline_id: Option<PipelineId>,
    pub pipeline_map: HashMap<PipelineId, ScenePipeline, BuildHasherDefault<FnvHasher>>,
    pub pipeline_sizes: HashMap<PipelineId, LayerSize>,
    pub pipeline_auxiliary_lists: AuxiliaryListsMap,
    pub display_lists: HashMap<PipelineId, Vec<DisplayItem>, BuildHasherDefault<FnvHasher>>,
}

impl Scene {
    pub fn new() -> Scene {
        Scene {
            root_pipeline_id: None,
            pipeline_sizes: HashMap::new(),
            pipeline_map: HashMap::with_hasher(Default::default()),
            pipeline_auxiliary_lists: HashMap::with_hasher(Default::default()),
            display_lists: HashMap::with_hasher(Default::default()),
        }
    }

    pub fn set_root_pipeline_id(&mut self, pipeline_id: PipelineId) {
        self.root_pipeline_id = Some(pipeline_id);
    }

    pub fn set_root_display_list(&mut self,
                                 pipeline_id: PipelineId,
                                 epoch: Epoch,
                                 built_display_list: BuiltDisplayList,
                                 background_color: Option<ColorF>,
                                 viewport_size: LayerSize,
                                 auxiliary_lists: AuxiliaryLists) {
        self.pipeline_auxiliary_lists.insert(pipeline_id, auxiliary_lists);
        self.display_lists.insert(pipeline_id, built_display_list.all_display_items().to_vec());

        let new_pipeline = ScenePipeline {
            pipeline_id: pipeline_id,
            epoch: epoch,
            viewport_size: viewport_size,
            background_color: background_color,
        };

        self.pipeline_map.insert(pipeline_id, new_pipeline);
    }
}
<|endoftext|>


<commit_before>#![no_std]

use libtock_low_level_debug::{AlertCode, LowLevelDebug};
use libtock_platform::{ErrorCode, Syscalls};
use libtock_runtime::TockSyscalls;

#[panic_handler]
fn panic_handler(_info: &core::panic::PanicInfo) -> ! {
    // Signal a panic using the LowLevelDebug capsule (if available).
    LowLevelDebug::<TockSyscalls>::print_alert_code(AlertCode::Panic);

    // Exit with a non-zero exit code to indicate failure.
    TockSyscalls::exit_terminate(ErrorCode::Fail as u32);
}
<commit_msg>Add a TODO for the completion code logic<commit_after>#![no_std]

use libtock_low_level_debug::{AlertCode, LowLevelDebug};
use libtock_platform::{ErrorCode, Syscalls};
use libtock_runtime::TockSyscalls;

#[panic_handler]
fn panic_handler(_info: &core::panic::PanicInfo) -> ! {
    // Signal a panic using the LowLevelDebug capsule (if available).
    LowLevelDebug::<TockSyscalls>::print_alert_code(AlertCode::Panic);

    // Exit with a non-zero exit code to indicate failure.
    // TODO(kupiakos@google.com): Make this logic consistent with tock/tock#2914
    // when it is merged.
    TockSyscalls::exit_terminate(ErrorCode::Fail as u32);
}
<|endoftext|>


<commit_before>use std::cmp::max;
use std::io::Write;
use std::path::{Path, PathBuf};

use grep::matcher::{Captures, Matcher};
use grep::regex::{RegexMatcher, RegexMatcherBuilder};
use grep::searcher::{
    BinaryDetection, Searcher, SearcherBuilder, Sink, SinkContext, SinkContextKind, SinkMatch,
};
use ignore::Walk;
use lazy_static::lazy_static;
use regex::Regex;
use structopt::StructOpt;
use text_io::read;

#[derive(Debug, StructOpt)]
#[structopt(name = "fnr")]
/// Look for things, optionally replace them.
struct Opts {
    /// Run search in case insensitive mode
    #[structopt(short, long)]
    insensitive: bool,

    /// Print out replacements without actually performing them
    #[structopt(short, long)]
    dry_run: bool,

    #[structopt(short = "A", long, default_value = "0")]
    after: usize,
    #[structopt(short = "B", long, default_value = "0")]
    before: usize,
    #[structopt(short = "C", long, default_value = "0")]
    context: usize,

    #[structopt(short, long)]
    prompt: bool,

    /// What to search for.
    #[structopt(name = "FIND")]
    find: String,

    /// What to replace it with. Required unless `prompt` is set.
    #[structopt(name = "REPLACE")]
    replace: Option<String>,

    /// Locations to search. Current directory if not specified.
    #[structopt(name = "PATH", parse(from_os_str))]
    paths: Vec<PathBuf>,
}

#[derive(Debug, Clone)]
struct Line(u64, String);

#[derive(Debug)]
struct SearchMatch {
    line: Line,
    context_pre: Vec<Line>,
    context_post: Vec<Line>,
}

impl SearchMatch {
    fn as_change(&self) -> Change {
        return Change {
            line_number: self.line.0,
            new_line: self.line.1.clone(),
        };
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
enum MatchState {
    Init,
    Before,
    Match,
    After,
}

#[derive(Debug)]
struct SearchMatchCollector {
    state: MatchState,

    cur_context_pre: Vec<Line>,
    cur_context_post: Vec<Line>,
    cur_match_line: Option<Line>,

    matches: Vec<SearchMatch>,
}

impl SearchMatchCollector {
    fn new() -> SearchMatchCollector {
        return SearchMatchCollector {
            state: MatchState::Init,
            cur_match_line: None,
            cur_context_pre: Vec::new(),
            cur_context_post: Vec::new(),
            matches: Vec::new(),
        };
    }

    fn maybe_emit(&mut self) {
        if let Some(line) = &self.cur_match_line {
            // TODO: clones are wasteful here.
            let m = SearchMatch {
                line: line.clone(),
                context_pre: self.cur_context_pre.clone(),
                context_post: self.cur_context_post.clone(),
            };

            self.matches.push(m);
            self.reset();
        }
    }

    fn reset(&mut self) {
        self.state = MatchState::Init;

        self.cur_match_line = None;
        self.cur_context_pre.clear();
        self.cur_context_post.clear();
    }

    #[inline]
    fn transition(&mut self, next: MatchState) {
        println!("transition: {:?} -> {:?}", self, next);
        match (self.state, next) {
            // Beginning a new match or ending a previous one
            (MatchState::Match, MatchState::Before)       // Have before context, no after context
            | (MatchState::Match, MatchState::Match)      // No before context, no after context
            | (MatchState::After, MatchState::Before)     // Have before context, have after context
            | (MatchState::After, MatchState::Match) => { // Have after context, no before context
                self.maybe_emit();
            }

            (_prev, next) => {
                self.state = next;
            }
        }
    }

    fn collect(&mut self) -> &Vec<SearchMatch> {
        self.maybe_emit();
        return &self.matches;
    }
}

impl Drop for SearchMatchCollector {
    fn drop(&mut self) {
        self.maybe_emit();
    }
}

impl Sink for SearchMatchCollector {
    type Error = std::io::Error;

    fn matched(
        &mut self,
        _searcher: &Searcher,
        mat: &SinkMatch<'_>,
    ) -> Result<bool, std::io::Error> {
        self.transition(MatchState::Match);

        // TODO: handle errors
        let line = Line(
            mat.line_number().expect("grab line number"),
            String::from_utf8_lossy(mat.bytes()).to_string(),
        );

        self.cur_match_line = Some(line);

        Ok(true)
    }

    fn context(
        &mut self,
        _searcher: &Searcher,
        ctx: &SinkContext<'_>,
    ) -> Result<bool, std::io::Error> {
        // TODO: handle errors.
        // TODO: can we avoid the additional .to_string() by storing a COW instead?
        let line = Line(
            ctx.line_number().expect("grab line number"),
            String::from_utf8_lossy(ctx.bytes()).to_string(),
        );

        match ctx.kind() {
            &SinkContextKind::Before => {
                self.transition(MatchState::Before);
                self.cur_context_pre.push(line);
            }
            &SinkContextKind::After => {
                self.transition(MatchState::After);
                self.cur_context_post.push(line);
            }
            &SinkContextKind::Other => {}
        }

        Ok(true)
    }
}

trait Replacer {
    fn replace(&self, input: &str) -> Option<String>;
}

struct RegexReplacer<'a> {
    matcher: &'a RegexMatcher,
    template: &'a str,
}

#[derive(Debug)]
enum TemplatePart<'a> {
    Literal(&'a str),
    Capture(u64),
}

type Template<'a> = Vec<TemplatePart<'a>>;

// TODO: Clean this up
fn parse_template<'a>(t: &'a str) -> Template<'a> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"\$(\d+)").unwrap();
    }

    let capture_positions = RE.captures_iter(t).map(|cap| {
        let num = cap[1].parse::<u64>().expect("invalid capture number");
        let mat = cap.get(1).unwrap();
        (mat.start() - 1, mat.end(), num)
    });
    let mut pos = 0;
    let mut template = Vec::new();

    for (start, end, value) in capture_positions {
        if start > pos {
            template.push(TemplatePart::Literal(&t[pos..start]));
        }
        template.push(TemplatePart::Capture(value));
        pos = end;
    }

    if pos < t.len() {
        template.push(TemplatePart::Literal(&t[pos..t.len()]));
    }

    template
}

impl<'a> Replacer for RegexReplacer<'a> {
    fn replace(&self, input: &str) -> Option<String> {
        let mut caps = self.matcher.new_captures().unwrap();
        let mut dst = vec![];

        self.matcher
            .replace_with_captures(input.as_bytes(), &mut caps, &mut dst, |caps, dst| {
                caps.interpolate(
                    |name| self.matcher.capture_index(name),
                    input.as_bytes(),
                    self.template.as_bytes(),
                    dst,
                );
                false
            })
            .unwrap();

        Some(String::from_utf8_lossy(&dst).to_string())
    }
}

type MatchDecider = dyn Fn(&SearchMatch) -> ReplacementDecision;

struct Change {
    line_number: u64,
    // TODO: use slice here
    new_line: String,
}

struct SearchMatchProcessor<'a> {
    replacer: &'a dyn Replacer,
    acceptor: &'a MatchDecider,

    decide_all: Option<bool>,
}

impl<'a> SearchMatchProcessor<'a> {
    fn new(replacer: &'a dyn Replacer, acceptor: &'a MatchDecider) -> SearchMatchProcessor<'a> {
        return SearchMatchProcessor {
            replacer,
            acceptor,
            decide_all: None,
        };
    }

    fn handle_path(&mut self, path: &Path, matches: &Vec<SearchMatch>) {
        if matches.is_empty() || Some(false) == self.decide_all {
            return;
        }
        println!(
            "--- {}: {} match{}",
            path.display(),
            matches.len(),
            if matches.len() == 1 { "" } else { "es" }
        );
        let mut accept_remaining = false;

        let mut change_list = vec![];
        for m in matches {
            self.display(m);

            let change = if accept_remaining || Some(true) == self.decide_all {
                m.as_change()
            } else {
                match (self.acceptor)(m) {
                    ReplacementDecision::IgnoreThis => continue,
                    ReplacementDecision::IgnoreFile => break,
                    ReplacementDecision::IgnoreRest => {
                        self.decide_all = Some(false);
                        break;
                    }
                    ReplacementDecision::AcceptThis => m.as_change(),
                    ReplacementDecision::AcceptFile => {
                        accept_remaining = true;
                        m.as_change()
                    }
                    ReplacementDecision::EditThis => {
                        // TODO: implement this
                        m.as_change()
                    }
                }
            };
            change_list.push(change);
        }

        self.apply_changes(path, &change_list.into_iter());

        println!("");
    }

    fn display(&self, m: &SearchMatch) {
        for line in &m.context_pre {
            print!("    {}: {}", line.0, line.1);
        }

        // TODO: Multiple matches on same line
        print!("-   {}: {}", m.line.0, m.line.1);
        if let Some(replacement) = self.replacer.replace(&m.line.1) {
            print!("+   {}: {}", m.line.0, replacement);
        }

        // TODO: print gap between non-consecutive lines
        for line in &m.context_post {
            print!("    {}: {}", line.0, line.1);
        }
    }

    fn apply_changes(&self, path: &Path, changes: &dyn Iterator<Item = Change>) {}
}

#[derive(Debug)]
enum ReplacementDecision {
    IgnoreThis,
    IgnoreFile,
    IgnoreRest,
    AcceptThis,
    AcceptFile,
    EditThis,
}

fn prompt_for_decision() -> ReplacementDecision {
    loop {
        print!("Stage this replacement [Y,n,q,a,d,?] ");
        std::io::stdout().flush().unwrap();
        let line: String = read!("{}\n");
        // TODO: ^D should not result in acceptance

        return match line.as_str() {
            "y" | "Y" | "" => ReplacementDecision::AcceptThis,
            "n" => ReplacementDecision::IgnoreThis,
            // TODO: handle these somehow
            "q" => ReplacementDecision::IgnoreRest,
            "a" => ReplacementDecision::AcceptFile,
            "d" => ReplacementDecision::IgnoreFile,
            "e" => ReplacementDecision::EditThis,

            "?" | _ => {
                println!(
                    "
Y - replace this line
n - do not replace this line
q - quit; do not replace this line or any remaining ones
a - replace this line and all remaining ones in this file
d - do not replace this line nor any remaining ones in this file
e - edit this replacement
? - show help
"
                );
                continue;
            }
        };
    }
}

fn main() {
    let opts: Opts = Opts::from_args();
    println!("Parsed opts: {:?}", opts);

    let matcher = RegexMatcherBuilder::new()
        .case_insensitive(opts.insensitive)
        .build(&opts.find)
        .expect("bad pattern");

    let mut searcher = SearcherBuilder::new()
        .binary_detection(BinaryDetection::quit(0x00))
        .line_number(true)
        .before_context(max(opts.context, opts.before))
        .after_context(max(opts.context, opts.after))
        .build();

    // TODO: Confirm that template does not reference more capture groups than exist.
    let template_str = &opts.replace.unwrap();
    let replacer = RegexReplacer {
        matcher: &matcher,
        template: template_str,
    };

    let should_prompt = opts.prompt;
    let acceptor = if opts.dry_run {
        |_: &SearchMatch| ReplacementDecision::IgnoreThis
    } else if opts.prompt {
        |_: &SearchMatch| prompt_for_decision()
    } else {
        |_: &SearchMatch| ReplacementDecision::AcceptThis
    };
    let mut proc = SearchMatchProcessor::new(&replacer, &acceptor);

    for dir_entry in Walk::new("../") {
        let dir_entry = dir_entry.unwrap();
        let path = dir_entry.path();
        if !path.is_file() {
            continue;
        }

        let mut sink = SearchMatchCollector::new();

        searcher
            .search_path(&matcher, dir_entry.path(), &mut sink)
            .expect("search failed");

        let matches = sink.collect();
        proc.handle_path(path, matches);
    }

    println!("All done.");
}
<commit_msg>Handle directories given in args<commit_after>use std::cmp::max;
use std::io::Write;
use std::path::{Path, PathBuf};

use grep::matcher::{Captures, Matcher};
use grep::regex::{RegexMatcher, RegexMatcherBuilder};
use grep::searcher::{
    BinaryDetection, Searcher, SearcherBuilder, Sink, SinkContext, SinkContextKind, SinkMatch,
};
use ignore::Walk;
use lazy_static::lazy_static;
use regex::Regex;
use structopt::StructOpt;
use text_io::read;

#[derive(Debug, StructOpt)]
#[structopt(name = "fnr")]
/// Look for things, optionally replace them.
struct Opts {
    /// Run search in case insensitive mode
    #[structopt(short, long)]
    insensitive: bool,

    /// Print out replacements without actually performing them
    #[structopt(short, long)]
    dry_run: bool,

    #[structopt(short = "A", long, default_value = "0")]
    after: usize,
    #[structopt(short = "B", long, default_value = "0")]
    before: usize,
    #[structopt(short = "C", long, default_value = "0")]
    context: usize,

    #[structopt(short, long)]
    prompt: bool,

    /// What to search for.
    #[structopt(name = "FIND")]
    find: String,

    /// What to replace it with. Required unless `prompt` is set.
    #[structopt(name = "REPLACE")]
    replace: Option<String>,

    /// Locations to search. Current directory if not specified.
    #[structopt(name = "PATH", parse(from_os_str))]
    paths: Vec<PathBuf>,
}

#[derive(Debug, Clone)]
struct Line(u64, String);

#[derive(Debug)]
struct SearchMatch {
    line: Line,
    context_pre: Vec<Line>,
    context_post: Vec<Line>,
}

impl SearchMatch {
    fn as_change(&self) -> Change {
        return Change {
            line_number: self.line.0,
            new_line: self.line.1.clone(),
        };
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
enum MatchState {
    Init,
    Before,
    Match,
    After,
}

#[derive(Debug)]
struct SearchMatchCollector {
    state: MatchState,

    cur_context_pre: Vec<Line>,
    cur_context_post: Vec<Line>,
    cur_match_line: Option<Line>,

    matches: Vec<SearchMatch>,
}

impl SearchMatchCollector {
    fn new() -> SearchMatchCollector {
        return SearchMatchCollector {
            state: MatchState::Init,
            cur_match_line: None,
            cur_context_pre: Vec::new(),
            cur_context_post: Vec::new(),
            matches: Vec::new(),
        };
    }

    fn maybe_emit(&mut self) {
        if let Some(line) = &self.cur_match_line {
            // TODO: clones are wasteful here.
            let m = SearchMatch {
                line: line.clone(),
                context_pre: self.cur_context_pre.clone(),
                context_post: self.cur_context_post.clone(),
            };

            self.matches.push(m);
            self.reset();
        }
    }

    fn reset(&mut self) {
        self.state = MatchState::Init;

        self.cur_match_line = None;
        self.cur_context_pre.clear();
        self.cur_context_post.clear();
    }

    #[inline]
    fn transition(&mut self, next: MatchState) {
        println!("transition: {:?} -> {:?}", self, next);
        match (self.state, next) {
            // Beginning a new match or ending a previous one
            (MatchState::Match, MatchState::Before)       // Have before context, no after context
            | (MatchState::Match, MatchState::Match)      // No before context, no after context
            | (MatchState::After, MatchState::Before)     // Have before context, have after context
            | (MatchState::After, MatchState::Match) => { // Have after context, no before context
                self.maybe_emit();
            }

            (_prev, next) => {
                self.state = next;
            }
        }
    }

    fn collect(&mut self) -> &Vec<SearchMatch> {
        self.maybe_emit();
        return &self.matches;
    }
}

impl Drop for SearchMatchCollector {
    fn drop(&mut self) {
        self.maybe_emit();
    }
}

impl Sink for SearchMatchCollector {
    type Error = std::io::Error;

    fn matched(
        &mut self,
        _searcher: &Searcher,
        mat: &SinkMatch<'_>,
    ) -> Result<bool, std::io::Error> {
        self.transition(MatchState::Match);

        // TODO: handle errors
        let line = Line(
            mat.line_number().expect("grab line number"),
            String::from_utf8_lossy(mat.bytes()).to_string(),
        );

        self.cur_match_line = Some(line);

        Ok(true)
    }

    fn context(
        &mut self,
        _searcher: &Searcher,
        ctx: &SinkContext<'_>,
    ) -> Result<bool, std::io::Error> {
        // TODO: handle errors.
        // TODO: can we avoid the additional .to_string() by storing a COW instead?
        let line = Line(
            ctx.line_number().expect("grab line number"),
            String::from_utf8_lossy(ctx.bytes()).to_string(),
        );

        match ctx.kind() {
            &SinkContextKind::Before => {
                self.transition(MatchState::Before);
                self.cur_context_pre.push(line);
            }
            &SinkContextKind::After => {
                self.transition(MatchState::After);
                self.cur_context_post.push(line);
            }
            &SinkContextKind::Other => {}
        }

        Ok(true)
    }
}

trait Replacer {
    fn replace(&self, input: &str) -> Option<String>;
}

struct RegexReplacer<'a> {
    matcher: &'a RegexMatcher,
    template: &'a str,
}

#[derive(Debug)]
enum TemplatePart<'a> {
    Literal(&'a str),
    Capture(u64),
}

type Template<'a> = Vec<TemplatePart<'a>>;

// TODO: Clean this up
fn parse_template<'a>(t: &'a str) -> Template<'a> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"\$(\d+)").unwrap();
    }

    let capture_positions = RE.captures_iter(t).map(|cap| {
        let num = cap[1].parse::<u64>().expect("invalid capture number");
        let mat = cap.get(1).unwrap();
        (mat.start() - 1, mat.end(), num)
    });
    let mut pos = 0;
    let mut template = Vec::new();

    for (start, end, value) in capture_positions {
        if start > pos {
            template.push(TemplatePart::Literal(&t[pos..start]));
        }
        template.push(TemplatePart::Capture(value));
        pos = end;
    }

    if pos < t.len() {
        template.push(TemplatePart::Literal(&t[pos..t.len()]));
    }

    template
}

impl<'a> Replacer for RegexReplacer<'a> {
    fn replace(&self, input: &str) -> Option<String> {
        let mut caps = self.matcher.new_captures().unwrap();
        let mut dst = vec![];

        self.matcher
            .replace_with_captures(input.as_bytes(), &mut caps, &mut dst, |caps, dst| {
                caps.interpolate(
                    |name| self.matcher.capture_index(name),
                    input.as_bytes(),
                    self.template.as_bytes(),
                    dst,
                );
                false
            })
            .unwrap();

        Some(String::from_utf8_lossy(&dst).to_string())
    }
}

type MatchDecider = dyn Fn(&SearchMatch) -> ReplacementDecision;

struct Change {
    line_number: u64,
    // TODO: use slice here
    new_line: String,
}

struct SearchMatchProcessor<'a> {
    replacer: &'a dyn Replacer,
    acceptor: &'a MatchDecider,

    decide_all: Option<bool>,
}

impl<'a> SearchMatchProcessor<'a> {
    fn new(replacer: &'a dyn Replacer, acceptor: &'a MatchDecider) -> SearchMatchProcessor<'a> {
        return SearchMatchProcessor {
            replacer,
            acceptor,
            decide_all: None,
        };
    }

    fn handle_path(&mut self, path: &Path, matches: &Vec<SearchMatch>) {
        if matches.is_empty() || Some(false) == self.decide_all {
            return;
        }
        println!(
            "--- {}: {} match{}",
            path.display(),
            matches.len(),
            if matches.len() == 1 { "" } else { "es" }
        );
        let mut accept_remaining = false;

        let mut change_list = vec![];
        for m in matches {
            self.display(m);

            let change = if accept_remaining || Some(true) == self.decide_all {
                m.as_change()
            } else {
                match (self.acceptor)(m) {
                    ReplacementDecision::IgnoreThis => continue,
                    ReplacementDecision::IgnoreFile => break,
                    ReplacementDecision::IgnoreRest => {
                        self.decide_all = Some(false);
                        break;
                    }
                    ReplacementDecision::AcceptThis => m.as_change(),
                    ReplacementDecision::AcceptFile => {
                        accept_remaining = true;
                        m.as_change()
                    }
                    ReplacementDecision::EditThis => {
                        // TODO: implement this
                        m.as_change()
                    }
                }
            };
            change_list.push(change);
        }

        self.apply_changes(path, &change_list.into_iter());

        println!("");
    }

    fn display(&self, m: &SearchMatch) {
        for line in &m.context_pre {
            print!("    {}: {}", line.0, line.1);
        }

        // TODO: Multiple matches on same line
        print!("-   {}: {}", m.line.0, m.line.1);
        if let Some(replacement) = self.replacer.replace(&m.line.1) {
            print!("+   {}: {}", m.line.0, replacement);
        }

        // TODO: print gap between non-consecutive lines
        for line in &m.context_post {
            print!("    {}: {}", line.0, line.1);
        }
    }

    fn apply_changes(&self, path: &Path, changes: &dyn Iterator<Item = Change>) {}
}

#[derive(Debug)]
enum ReplacementDecision {
    IgnoreThis,
    IgnoreFile,
    IgnoreRest,
    AcceptThis,
    AcceptFile,
    EditThis,
}

fn prompt_for_decision() -> ReplacementDecision {
    loop {
        print!("Stage this replacement [Y,n,q,a,d,?] ");
        std::io::stdout().flush().unwrap();
        let line: String = read!("{}\n");
        // TODO: ^D should not result in acceptance

        return match line.as_str() {
            "y" | "Y" | "" => ReplacementDecision::AcceptThis,
            "n" => ReplacementDecision::IgnoreThis,
            // TODO: handle these somehow
            "q" => ReplacementDecision::IgnoreRest,
            "a" => ReplacementDecision::AcceptFile,
            "d" => ReplacementDecision::IgnoreFile,
            "e" => ReplacementDecision::EditThis,

            "?" | _ => {
                println!(
                    "
Y - replace this line
n - do not replace this line
q - quit; do not replace this line or any remaining ones
a - replace this line and all remaining ones in this file
d - do not replace this line nor any remaining ones in this file
e - edit this replacement
? - show help
"
                );
                continue;
            }
        };
    }
}

fn main() {
    let opts: Opts = Opts::from_args();
    println!("Parsed opts: {:?}", opts);

    let matcher = RegexMatcherBuilder::new()
        .case_insensitive(opts.insensitive)
        .build(&opts.find)
        .expect("bad pattern");

    let mut searcher = SearcherBuilder::new()
        .binary_detection(BinaryDetection::quit(0x00))
        .line_number(true)
        .before_context(max(opts.context, opts.before))
        .after_context(max(opts.context, opts.after))
        .build();

    // TODO: Confirm that template does not reference more capture groups than exist.
    let template_str = &opts.replace.unwrap();
    let replacer = RegexReplacer {
        matcher: &matcher,
        template: template_str,
    };

    let should_prompt = opts.prompt;
    let acceptor = if opts.dry_run {
        |_: &SearchMatch| ReplacementDecision::IgnoreThis
    } else if opts.prompt {
        |_: &SearchMatch| prompt_for_decision()
    } else {
        |_: &SearchMatch| ReplacementDecision::AcceptThis
    };
    let mut proc = SearchMatchProcessor::new(&replacer, &acceptor);

    let paths = if opts.paths.is_empty() {
        vec![Path::new("./").to_owned()]
    } else {
        opts.paths
    };

    for path in paths {
        // TODO: Support files as well as directories here
        for dir_entry in Walk::new(path) {
            let dir_entry = dir_entry.unwrap();
            let path = dir_entry.path();
            if !path.is_file() {
                continue;
            }

            let mut sink = SearchMatchCollector::new();

            searcher
                .search_path(&matcher, dir_entry.path(), &mut sink)
                .expect("search failed");

            let matches = sink.collect();
            proc.handle_path(path, matches);
        }
    }

    println!("All done.");
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!
 * This module contains a simple utility routine
 * used by both `typeck` and `const_eval`.
 * Almost certainly this could (and should) be refactored out of existence.
 */

use middle::def;
use middle::ty::{self, Ty};

use syntax::codemap::Span;
use rustc_front::hir as ast;

pub fn prohibit_type_params(tcx: &ty::ctxt, segments: &[ast::PathSegment]) {
    for segment in segments {
        for typ in segment.parameters.types() {
            span_err!(tcx.sess, typ.span, E0109,
                      "type parameters are not allowed on this type");
            break;
        }
        for lifetime in segment.parameters.lifetimes() {
            span_err!(tcx.sess, lifetime.span, E0110,
                      "lifetime parameters are not allowed on this type");
            break;
        }
        for binding in segment.parameters.bindings() {
            prohibit_projection(tcx, binding.span);
            break;
        }
    }
}

pub fn prohibit_projection(tcx: &ty::ctxt, span: Span)
{
    span_err!(tcx.sess, span, E0229,
              "associated type bindings are not allowed here");
}

pub fn prim_ty_to_ty<'tcx>(tcx: &ty::ctxt<'tcx>,
                           segments: &[ast::PathSegment],
                           nty: ast::PrimTy)
                           -> Ty<'tcx> {
    prohibit_type_params(tcx, segments);
    match nty {
        ast::TyBool => tcx.types.bool,
        ast::TyChar => tcx.types.char,
        ast::TyInt(it) => tcx.mk_mach_int(it),
        ast::TyUint(uit) => tcx.mk_mach_uint(uit),
        ast::TyFloat(ft) => tcx.mk_mach_float(ft),
        ast::TyStr => tcx.mk_str()
    }
}

pub fn ast_ty_to_prim_ty<'tcx>(tcx: &ty::ctxt<'tcx>, ast_ty: &ast::Ty)
                               -> Option<Ty<'tcx>> {
    if let ast::TyPath(None, ref path) = ast_ty.node {
        let def = match tcx.def_map.borrow().get(&ast_ty.id) {
            None => {
                tcx.sess.span_bug(ast_ty.span,
                                  &format!("unbound path {:?}", path))
            }
            Some(d) => d.full_def()
        };
        if let def::DefPrimTy(nty) = def {
            Some(prim_ty_to_ty(tcx, &path.segments, nty))
        } else {
            None
        }
    } else {
        None
    }
}
<commit_msg>Add docs for `ast_ty_to_prim_ty`<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!
 * This module contains a simple utility routine
 * used by both `typeck` and `const_eval`.
 * Almost certainly this could (and should) be refactored out of existence.
 */

use middle::def;
use middle::ty::{self, Ty};

use syntax::codemap::Span;
use rustc_front::hir as ast;

pub fn prohibit_type_params(tcx: &ty::ctxt, segments: &[ast::PathSegment]) {
    for segment in segments {
        for typ in segment.parameters.types() {
            span_err!(tcx.sess, typ.span, E0109,
                      "type parameters are not allowed on this type");
            break;
        }
        for lifetime in segment.parameters.lifetimes() {
            span_err!(tcx.sess, lifetime.span, E0110,
                      "lifetime parameters are not allowed on this type");
            break;
        }
        for binding in segment.parameters.bindings() {
            prohibit_projection(tcx, binding.span);
            break;
        }
    }
}

pub fn prohibit_projection(tcx: &ty::ctxt, span: Span)
{
    span_err!(tcx.sess, span, E0229,
              "associated type bindings are not allowed here");
}

pub fn prim_ty_to_ty<'tcx>(tcx: &ty::ctxt<'tcx>,
                           segments: &[ast::PathSegment],
                           nty: ast::PrimTy)
                           -> Ty<'tcx> {
    prohibit_type_params(tcx, segments);
    match nty {
        ast::TyBool => tcx.types.bool,
        ast::TyChar => tcx.types.char,
        ast::TyInt(it) => tcx.mk_mach_int(it),
        ast::TyUint(uit) => tcx.mk_mach_uint(uit),
        ast::TyFloat(ft) => tcx.mk_mach_float(ft),
        ast::TyStr => tcx.mk_str()
    }
}

/// If a type in the AST is a primitive type, return the ty::Ty corresponding
/// to it.
pub fn ast_ty_to_prim_ty<'tcx>(tcx: &ty::ctxt<'tcx>, ast_ty: &ast::Ty)
                               -> Option<Ty<'tcx>> {
    if let ast::TyPath(None, ref path) = ast_ty.node {
        let def = match tcx.def_map.borrow().get(&ast_ty.id) {
            None => {
                tcx.sess.span_bug(ast_ty.span,
                                  &format!("unbound path {:?}", path))
            }
            Some(d) => d.full_def()
        };
        if let def::DefPrimTy(nty) = def {
            Some(prim_ty_to_ty(tcx, &path.segments, nty))
        } else {
            None
        }
    } else {
        None
    }
}
<|endoftext|>


<commit_before>// https://leetcode.com/problems/create-maximum-number/
pub fn max_number(nums1: Vec<i32>, nums2: Vec<i32>, k: i32) -> Vec<i32> {
    todo!()
}

fn main() {
    println!(
        "{:?}",
        max_number(vec![3, 4, 6, 5], vec![9, 1, 2, 5, 8, 3], 5)
    ); // [9,8,6,5,3]
    println!("{:?}", max_number(vec![6, 7], vec![6, 0, 4], 5)); // [6,7,6,0,4]
    println!("{:?}", max_number(vec![3, 9], vec![8, 9], 3)); // [9,8,9]
    println!("{:?}", max_number(vec![3, 9], vec![8, 9], 3)); // [9,8,9]
    println!("{:?}", max_number(vec![9, 8, 7], vec![1, 2], 4)); // [9,8,7,2]
    println!(
        "{:?}",
        max_number(
            vec![5, 9, 3, 7, 5, 6, 2, 3],
            vec![3, 8, 1, 2, 8, 6, 0, 8],
            8
        )
    ); // [9,8,8,8,7,6,2,3]
}
<commit_msg>Update problem 321<commit_after>// https://leetcode.com/problems/create-maximum-number/
pub fn max_number(nums1: Vec<i32>, nums2: Vec<i32>, k: i32) -> Vec<i32> {
    todo!()
}

fn main() {
    println!(
        "{:?}",
        max_number(vec![3, 4, 6, 5], vec![9, 1, 2, 5, 8, 3], 5)
    ); // [9,8,6,5,3]
    println!("{:?}", max_number(vec![6, 7], vec![6, 0, 4], 5)); // [6,7,6,0,4]
    println!("{:?}", max_number(vec![3, 9], vec![8, 9], 3)); // [9,8,9]
    println!("{:?}", max_number(vec![3, 9], vec![8, 9], 3)); // [9,8,9]
    println!("{:?}", max_number(vec![9, 8, 7], vec![1, 2], 4)); // [9,8,7,2]
    println!("{:?}", max_number(vec![3, 4, 6, 9], vec![9, 2, 5, 8, 9], 4)); // [9,9,5,8,9]
    println!(
        "{:?}",
        max_number(
            vec![5, 9, 3, 7, 5, 6, 2, 3],
            vec![3, 8, 1, 2, 8, 6, 0, 8],
            8
        )
    ); // [9,8,8,8,7,6,2,3]
}
<|endoftext|>