<commit_before>use std::{
    ops::Deref,
    sync::{
        atomic::{
            AtomicUsize,
            Ordering::{Acquire, Relaxed, Release},
        },
        Arc, Mutex, RwLock,
    },
};

use pagecache::{FastMap8, PagePtr};

use super::*;

/// The `sled` embedded database!
#[derive(Clone)]
pub struct Db {
    config: Config,
    pages: Arc<PageCache<BLinkMaterializer, Frag, Recovery>>,
    idgen: Arc<AtomicUsize>,
    idgen_persists: Arc<AtomicUsize>,
    idgen_persist_mu: Arc<Mutex<()>>,
    tenants: Arc<RwLock<FastMap8<Vec<u8>, Arc<Tree>>>>,
    default: Arc<Tree>,
    transactions: Arc<Tree>,
    was_recovered: bool,
}

unsafe impl Send for Db {}

unsafe impl Sync for Db {}

impl Drop for Db {
    fn drop(&mut self) {
        if let Err(e) = self.pages.flush() {
            error!(
                "failed to flush underlying \
                 pagecache during drop: {:?}",
                e
            );
        }

        #[cfg(feature = "event_log")]
        {
            let guard = pin();

            self.config.event_log.meta_before_restart(
                meta::meta(&*self.pages, &guard)
                    .expect("should get meta under test")
                    .clone(),
            );
        }
    }
}

impl Deref for Db {
    type Target = Tree;

    fn deref(&self) -> &Tree {
        &self.default
    }
}

impl Db {
    /// Load existing or create a new `Db` with a default configuration.
    pub fn start_default<P: AsRef<std::path::Path>>(
        path: P,
    ) -> Result<Db, ()> {
        let config = ConfigBuilder::new().path(path).build();
        Self::start(config)
    }

    /// Load existing or create a new `Db`.
    pub fn start(config: Config) -> Result<Db, ()> {
        let _measure = Measure::new(&M.tree_start);

        #[cfg(any(test, feature = "check_snapshot_integrity"))]
        match config
            .verify_snapshot::<BLinkMaterializer, Frag, Recovery>()
        {
            Ok(_) => {}
            #[cfg(feature = "failpoints")]
            Err(Error::FailPoint) => {}
            other => panic!("failed to verify snapshot: {:?}", other),
        }

        let pages = Arc::new(PageCache::start(config.clone())?);

        let recovery: Recovery =
            pages.recovered_state().unwrap_or_default();
        let idgen_recovery =
            recovery.counter + (2 * config.idgen_persist_interval);
        let idgen_persists = recovery.counter
            / config.idgen_persist_interval
            * config.idgen_persist_interval;

        let guard = pin();

        let was_recovered: bool;

        if pages
            .get(META_PID, &guard)
            .map_err(|e| e.danger_cast())?
            .is_unallocated()
        {
            was_recovered = false;

            // set up meta
            let meta_id = pages.allocate(&guard)?;

            assert_eq!(
                meta_id,
                META_PID,
                "we expect the meta page to have pid {}, but it had pid {} instead",
                META_PID,
                meta_id,
            );

            let meta = Frag::Meta(Meta::default());
            pages
                .replace(meta_id, PagePtr::allocated(), meta, &guard)
                .map_err(|e| e.danger_cast())?;

            // set up idgen
            let counter_id = pages.allocate(&guard)?;

            assert_eq!(
                counter_id,
                COUNTER_PID,
                "we expect the counter to have pid {}, but it had pid {} instead",
                COUNTER_PID,
                counter_id,
            );

            let counter = Frag::Counter(0);
            pages
                .replace(
                    counter_id,
                    PagePtr::allocated(),
                    counter,
                    &guard,
                )
                .map_err(|e| e.danger_cast())?;
        } else {
            was_recovered = true;
        };

        let default_tree = meta::open_tree(
            pages.clone(),
            config.clone(),
            DEFAULT_TREE_ID.to_vec(),
            &guard,
        )?;
        let tx_tree = meta::open_tree(
            pages.clone(),
            config.clone(),
            TX_TREE_ID.to_vec(),
            &guard,
        )?;

        let ret = Db {
            pages: pages,
            config,
            idgen: Arc::new(AtomicUsize::new(idgen_recovery)),
            idgen_persists: Arc::new(AtomicUsize::new(
                idgen_persists,
            )),
            idgen_persist_mu: Arc::new(Mutex::new(())),
            tenants: Arc::new(RwLock::new(FastMap8::default())),
            default: Arc::new(default_tree),
            transactions: Arc::new(tx_tree),
            was_recovered,
        };

        #[cfg(feature = "event_log")]
        ret.config.event_log.meta_after_restart(
            meta::meta(&*ret.pages, &guard)
                .expect("should be able to get meta under test")
                .clone(),
        );

        let mut tenants = ret.tenants.write().unwrap();

        for (id, _root) in
            meta::meta(&*ret.pages, &guard)?.tenants().into_iter()
        {
            let tree = Tree {
                tree_id: id.clone(),
                subscriptions: Arc::new(Subscriptions::default()),
                config: ret.config.clone(),
                pages: ret.pages.clone(),
            };
            tenants.insert(id, Arc::new(tree));
        }

        drop(tenants);

        let mut ret = ret;

        ret.default = ret.open_tree(DEFAULT_TREE_ID.to_vec())?;
        ret.transactions = ret.open_tree(TX_TREE_ID.to_vec())?;

        Ok(ret)
    }

    /// Generate a monotonic ID. Not guaranteed to be
    /// contiguous. Written to disk every `idgen_persist_interval`
    /// operations, followed by a blocking flush. During recovery, we
    /// take the last recovered generated ID and add 2x
    /// the `idgen_persist_interval` to it. While persisting, if the
    /// previous persisted counter wasn't synced to disk yet, we will do
    /// a blocking flush to fsync the latest counter, ensuring
    /// that we will never give out the same counter twice.
    pub fn generate_id(&self) -> Result<usize, ()> {
        let ret = self.idgen.fetch_add(1, Relaxed);

        let interval = self.config.idgen_persist_interval;
        let necessary_persists = ret / interval * interval;
        let mut persisted = self.idgen_persists.load(Acquire);

        while persisted < necessary_persists {
            let _mu = self.idgen_persist_mu.lock().unwrap();
            persisted = self.idgen_persists.load(Acquire);
            if persisted < necessary_persists {
                // it's our responsibility to persist up to our ID
                let guard = pin();
                let (current, key) = self
                    .pages
                    .get(COUNTER_PID, &guard)
                    .map_err(|e| e.danger_cast())?
                    .unwrap();

                if let Frag::Counter(current) = current {
                    assert_eq!(*current, persisted);
                } else {
                    panic!(
                        "counter pid contained non-Counter: {:?}",
                        current
                    );
                }

                let counter_frag = Frag::Counter(necessary_persists);

                let old = self
                    .idgen_persists
                    .swap(necessary_persists, Release);
                assert_eq!(old, persisted);

                self.pages
                    .replace(
                        COUNTER_PID,
                        key.clone(),
                        counter_frag,
                        &guard,
                    )
                    .map_err(|e| e.danger_cast())?;

                // during recovery we add 2x the interval. we only
                // need to block if the last one wasn't stable yet.
                if key.last_lsn() > self.pages.stable_lsn() {
                    self.pages.make_stable(key.last_lsn())?;
                }

                guard.flush();
            }
        }

        Ok(ret)
    }

    /// Open or create a new disk-backed Tree with its own keyspace,
    /// accessible from the `Db` via the provided identifier.
    pub fn open_tree(&self, name: Vec<u8>) -> Result<Arc<Tree>, ()> {
        let guard = pin();

        let tenants = self.tenants.read().unwrap();
        if let Some(tree) = tenants.get(&name) {
            return Ok(tree.clone());
        }
        // drop reader lock
        drop(tenants);

        // set up empty leaf
        let leaf_id = self.pages.allocate(&guard)?;
        trace!(
            "allocated pid {} for leaf in open_tree for namespace {:?}",
            leaf_id,
            name,
        );

        let leaf = Frag::Base(Node {
            id: leaf_id,
            data: Data::Leaf(vec![]),
            next: None,
            lo: vec![].into(),
            hi: vec![].into(),
        });

        self.pages
            .replace(leaf_id, PagePtr::allocated(), leaf, &guard)
            .map_err(|e| e.danger_cast())?;

        // set up root index
        let root_id = self.pages.allocate(&guard)?;

        debug!(
            "allocated pid {} for root of new_tree {:?}",
            root_id, name
        );

        // vec![0] represents a prefix-encoded empty prefix
        let root_index_vec = vec![(vec![0].into(), leaf_id)];

        let root = Frag::Base(Node {
            id: root_id,
            data: Data::Index(root_index_vec),
            next: None,
            lo: vec![].into(),
            hi: vec![].into(),
        });

        self.pages
            .replace(root_id, PagePtr::allocated(), root, &guard)
            .map_err(|e| e.danger_cast())?;

        meta::cas_root(
            &*self.pages,
            name.clone(),
            None,
            Some(root_id),
            &guard,
        )
        .map_err(|e| e.danger_cast())?;

        let mut tenants = self.tenants.write().unwrap();
        let tree = Arc::new(Tree {
            tree_id: name.clone(),
            subscriptions: Arc::new(Subscriptions::default()),
            config: self.config.clone(),
            pages: self.pages.clone(),
        });
        tenants.insert(name, tree.clone());

        Ok(tree)
    }

    /// Remove a disk-backed collection.
    pub fn drop_tree(&self, name: &[u8]) -> Result<bool, ()> {
        if name == DEFAULT_TREE_ID || name == TX_TREE_ID {
            return Err(Error::Unsupported(
                "cannot remove the core structures".into(),
            ));
        }
        trace!("dropping tree {:?}", name,);

        let mut tenants = self.tenants.write().unwrap();

        let tree = if let Some(tree) = tenants.remove(&*name) {
            tree
        } else {
            return Ok(false);
        };

        let guard = pin();

        let mut root_id =
            meta::pid_for_name(&*self.pages, &name, &guard)?;

        let leftmost_chain: Vec<PageId> = tree
            .path_for_key(b"", &guard)?
            .into_iter()
            .map(|(frag, _tp)| frag.unwrap_base().id)
            .collect();

        loop {
            let res = meta::cas_root(
                &*self.pages,
                name.to_vec(),
                Some(root_id),
                None,
                &guard,
            )
            .map_err(|e| e.danger_cast());

            match res {
                Ok(_) => break,
                Err(Error::CasFailed(actual)) => root_id = actual,
                Err(other) => return Err(other.danger_cast()),
            }
        }

        // drop writer lock
        drop(tenants);

        guard.defer(move || tree.gc_pages(leftmost_chain));

        guard.flush();

        Ok(true)
    }

    /// Returns `true` if the database was
    /// recovered from a previous process.
    /// Note that database state is only
    /// guaranteed to be present up to the
    /// last call to `flush`! Otherwise state
    /// is synced to disk periodically if the
    /// `sync_every_ms` configuration option
    /// is set to `Some(number_of_ms_between_syncs)`
    /// or if the IO buffer gets filled to
    /// capacity before being rotated.
    pub fn was_recovered(&self) -> bool {
        self.was_recovered
    }

    /// Record a set of pages as being involved in
    /// a transaction that is about to be written to
    /// one of the structures in the Db. Do this
    /// before performing transactions in case
    /// the system crashes before all changes
    /// can be written. During recovery,
    /// you can call `possibly_dirty_pages`
    /// to get the set of pages that may have
    /// transactional state written to them.
    pub fn mark_pending_tx(
        &self,
        _pages: Vec<PageId>,
        _txid: u64,
    ) -> Result<(), ()> {
        unimplemented!()
    }

    /// Mark a transaction as being aborted. This
    /// ensures that during recovery we know that
    /// this transaction is not the last one that
    /// should be recovered, because the process
    /// finished the transaction, but we may need
    /// to clean up its pending state still.
    pub fn abort_tx(&self, _txid: u64) -> Result<(), ()> {
        unimplemented!()
    }

    /// Mark a transaction as being completed. This
    /// ensures that during recovery we know that
    /// this transaction completed, and that we
    /// can safely remove its entry in the
    /// transaction table.
    pub fn commit_tx(&self, _txid: u64) -> Result<(), ()> {
        unimplemented!()
    }

    /// Remove entries associated with this transaction
    /// in the transaction table, because we have
    /// completed the transaction and finished
    /// applying or removing pending state, and do
    /// not need to know about it during crash recovery.
    pub fn cleanup_tx(&self, _txid: u64) -> Result<(), ()> {
        unimplemented!()
    }

    /// Call this during recovery to retrieve the set of
    /// pages that were involved in a transaction
    /// that was in a pending or aborted (but not
    /// yet fully cleaned up) state during process
    /// termination.
    pub fn possibly_dirty_pages(
        &self,
    ) -> Result<Vec<(u64, Vec<PageId>)>, ()> {
        unimplemented!()
    }
}
<commit_msg>Properly handle crashes before the META page is able to transition from Allocated to replacement<commit_after>use std::{
    ops::Deref,
    sync::{
        atomic::{
            AtomicUsize,
            Ordering::{Acquire, Relaxed, Release},
        },
        Arc, Mutex, RwLock,
    },
};

use pagecache::{FastMap8, PagePtr};

use super::*;

/// The `sled` embedded database!
#[derive(Clone)]
pub struct Db {
    config: Config,
    pages: Arc<PageCache<BLinkMaterializer, Frag, Recovery>>,
    idgen: Arc<AtomicUsize>,
    idgen_persists: Arc<AtomicUsize>,
    idgen_persist_mu: Arc<Mutex<()>>,
    tenants: Arc<RwLock<FastMap8<Vec<u8>, Arc<Tree>>>>,
    default: Arc<Tree>,
    transactions: Arc<Tree>,
    was_recovered: bool,
}

unsafe impl Send for Db {}

unsafe impl Sync for Db {}

impl Drop for Db {
    fn drop(&mut self) {
        if let Err(e) = self.pages.flush() {
            error!(
                "failed to flush underlying \
                 pagecache during drop: {:?}",
                e
            );
        }

        #[cfg(feature = "event_log")]
        {
            let guard = pin();

            self.config.event_log.meta_before_restart(
                meta::meta(&*self.pages, &guard)
                    .expect("should get meta under test")
                    .clone(),
            );
        }
    }
}

impl Deref for Db {
    type Target = Tree;

    fn deref(&self) -> &Tree {
        &self.default
    }
}

impl Db {
    /// Load existing or create a new `Db` with a default configuration.
    pub fn start_default<P: AsRef<std::path::Path>>(
        path: P,
    ) -> Result<Db, ()> {
        let config = ConfigBuilder::new().path(path).build();
        Self::start(config)
    }

    /// Load existing or create a new `Db`.
    pub fn start(config: Config) -> Result<Db, ()> {
        let _measure = Measure::new(&M.tree_start);

        #[cfg(any(test, feature = "check_snapshot_integrity"))]
        match config
            .verify_snapshot::<BLinkMaterializer, Frag, Recovery>()
        {
            Ok(_) => {}
            #[cfg(feature = "failpoints")]
            Err(Error::FailPoint) => {}
            other => panic!("failed to verify snapshot: {:?}", other),
        }

        let pages = Arc::new(PageCache::start(config.clone())?);

        let recovery: Recovery =
            pages.recovered_state().unwrap_or_default();
        let idgen_recovery =
            recovery.counter + (2 * config.idgen_persist_interval);
        let idgen_persists = recovery.counter
            / config.idgen_persist_interval
            * config.idgen_persist_interval;

        let guard = pin();

        let was_recovered: bool;

        if pages
            .get(META_PID, &guard)
            .map_err(|e| e.danger_cast())?
            .is_unallocated()
        {
            // set up meta
            let meta_id = pages.allocate(&guard)?;

            assert_eq!(
                meta_id,
                META_PID,
                "we expect the meta page to have pid {}, but it had pid {} instead",
                META_PID,
                meta_id,
            );
        }

        if pages
            .get(META_PID, &guard)
            .map_err(|e| e.danger_cast())?
            .is_allocated()
        {
            was_recovered = false;

            let meta = Frag::Meta(Meta::default());
            pages
                .replace(META_PID, PagePtr::allocated(), meta, &guard)
                .map_err(|e| e.danger_cast())?;

            // set up idgen
            let counter_id = pages.allocate(&guard)?;

            assert_eq!(
                counter_id,
                COUNTER_PID,
                "we expect the counter to have pid {}, but it had pid {} instead",
                COUNTER_PID,
                counter_id,
            );

            let counter = Frag::Counter(0);
            pages
                .replace(
                    counter_id,
                    PagePtr::allocated(),
                    counter,
                    &guard,
                )
                .map_err(|e| e.danger_cast())?;
        } else {
            was_recovered = true;
        }

        let default_tree = meta::open_tree(
            pages.clone(),
            config.clone(),
            DEFAULT_TREE_ID.to_vec(),
            &guard,
        )?;
        let tx_tree = meta::open_tree(
            pages.clone(),
            config.clone(),
            TX_TREE_ID.to_vec(),
            &guard,
        )?;

        let ret = Db {
            pages: pages,
            config,
            idgen: Arc::new(AtomicUsize::new(idgen_recovery)),
            idgen_persists: Arc::new(AtomicUsize::new(
                idgen_persists,
            )),
            idgen_persist_mu: Arc::new(Mutex::new(())),
            tenants: Arc::new(RwLock::new(FastMap8::default())),
            default: Arc::new(default_tree),
            transactions: Arc::new(tx_tree),
            was_recovered,
        };

        #[cfg(feature = "event_log")]
        ret.config.event_log.meta_after_restart(
            meta::meta(&*ret.pages, &guard)
                .expect("should be able to get meta under test")
                .clone(),
        );

        let mut tenants = ret.tenants.write().unwrap();

        for (id, _root) in
            meta::meta(&*ret.pages, &guard)?.tenants().into_iter()
        {
            let tree = Tree {
                tree_id: id.clone(),
                subscriptions: Arc::new(Subscriptions::default()),
                config: ret.config.clone(),
                pages: ret.pages.clone(),
            };
            tenants.insert(id, Arc::new(tree));
        }

        drop(tenants);

        let mut ret = ret;

        ret.default = ret.open_tree(DEFAULT_TREE_ID.to_vec())?;
        ret.transactions = ret.open_tree(TX_TREE_ID.to_vec())?;

        Ok(ret)
    }

    /// Generate a monotonic ID. Not guaranteed to be
    /// contiguous. Written to disk every `idgen_persist_interval`
    /// operations, followed by a blocking flush. During recovery, we
    /// take the last recovered generated ID and add 2x
    /// the `idgen_persist_interval` to it. While persisting, if the
    /// previous persisted counter wasn't synced to disk yet, we will do
    /// a blocking flush to fsync the latest counter, ensuring
    /// that we will never give out the same counter twice.
    pub fn generate_id(&self) -> Result<usize, ()> {
        let ret = self.idgen.fetch_add(1, Relaxed);

        let interval = self.config.idgen_persist_interval;
        let necessary_persists = ret / interval * interval;
        let mut persisted = self.idgen_persists.load(Acquire);

        while persisted < necessary_persists {
            let _mu = self.idgen_persist_mu.lock().unwrap();
            persisted = self.idgen_persists.load(Acquire);
            if persisted < necessary_persists {
                // it's our responsibility to persist up to our ID
                let guard = pin();
                let (current, key) = self
                    .pages
                    .get(COUNTER_PID, &guard)
                    .map_err(|e| e.danger_cast())?
                    .unwrap();

                if let Frag::Counter(current) = current {
                    assert_eq!(*current, persisted);
                } else {
                    panic!(
                        "counter pid contained non-Counter: {:?}",
                        current
                    );
                }

                let counter_frag = Frag::Counter(necessary_persists);

                let old = self
                    .idgen_persists
                    .swap(necessary_persists, Release);
                assert_eq!(old, persisted);

                self.pages
                    .replace(
                        COUNTER_PID,
                        key.clone(),
                        counter_frag,
                        &guard,
                    )
                    .map_err(|e| e.danger_cast())?;

                // during recovery we add 2x the interval. we only
                // need to block if the last one wasn't stable yet.
                if key.last_lsn() > self.pages.stable_lsn() {
                    self.pages.make_stable(key.last_lsn())?;
                }

                guard.flush();
            }
        }

        Ok(ret)
    }

    /// Open or create a new disk-backed Tree with its own keyspace,
    /// accessible from the `Db` via the provided identifier.
    pub fn open_tree(&self, name: Vec<u8>) -> Result<Arc<Tree>, ()> {
        let guard = pin();

        let tenants = self.tenants.read().unwrap();
        if let Some(tree) = tenants.get(&name) {
            return Ok(tree.clone());
        }
        // drop reader lock
        drop(tenants);

        // set up empty leaf
        let leaf_id = self.pages.allocate(&guard)?;
        trace!(
            "allocated pid {} for leaf in open_tree for namespace {:?}",
            leaf_id,
            name,
        );

        let leaf = Frag::Base(Node {
            id: leaf_id,
            data: Data::Leaf(vec![]),
            next: None,
            lo: vec![].into(),
            hi: vec![].into(),
        });

        self.pages
            .replace(leaf_id, PagePtr::allocated(), leaf, &guard)
            .map_err(|e| e.danger_cast())?;

        // set up root index
        let root_id = self.pages.allocate(&guard)?;

        debug!(
            "allocated pid {} for root of new_tree {:?}",
            root_id, name
        );

        // vec![0] represents a prefix-encoded empty prefix
        let root_index_vec = vec![(vec![0].into(), leaf_id)];

        let root = Frag::Base(Node {
            id: root_id,
            data: Data::Index(root_index_vec),
            next: None,
            lo: vec![].into(),
            hi: vec![].into(),
        });

        self.pages
            .replace(root_id, PagePtr::allocated(), root, &guard)
            .map_err(|e| e.danger_cast())?;

        meta::cas_root(
            &*self.pages,
            name.clone(),
            None,
            Some(root_id),
            &guard,
        )
        .map_err(|e| e.danger_cast())?;

        let mut tenants = self.tenants.write().unwrap();
        let tree = Arc::new(Tree {
            tree_id: name.clone(),
            subscriptions: Arc::new(Subscriptions::default()),
            config: self.config.clone(),
            pages: self.pages.clone(),
        });
        tenants.insert(name, tree.clone());

        Ok(tree)
    }

    /// Remove a disk-backed collection.
    pub fn drop_tree(&self, name: &[u8]) -> Result<bool, ()> {
        if name == DEFAULT_TREE_ID || name == TX_TREE_ID {
            return Err(Error::Unsupported(
                "cannot remove the core structures".into(),
            ));
        }
        trace!("dropping tree {:?}", name,);

        let mut tenants = self.tenants.write().unwrap();

        let tree = if let Some(tree) = tenants.remove(&*name) {
            tree
        } else {
            return Ok(false);
        };

        let guard = pin();

        let mut root_id =
            meta::pid_for_name(&*self.pages, &name, &guard)?;

        let leftmost_chain: Vec<PageId> = tree
            .path_for_key(b"", &guard)?
            .into_iter()
            .map(|(frag, _tp)| frag.unwrap_base().id)
            .collect();

        loop {
            let res = meta::cas_root(
                &*self.pages,
                name.to_vec(),
                Some(root_id),
                None,
                &guard,
            )
            .map_err(|e| e.danger_cast());

            match res {
                Ok(_) => break,
                Err(Error::CasFailed(actual)) => root_id = actual,
                Err(other) => return Err(other.danger_cast()),
            }
        }

        // drop writer lock
        drop(tenants);

        guard.defer(move || tree.gc_pages(leftmost_chain));

        guard.flush();

        Ok(true)
    }

    /// Returns `true` if the database was
    /// recovered from a previous process.
    /// Note that database state is only
    /// guaranteed to be present up to the
    /// last call to `flush`! Otherwise state
    /// is synced to disk periodically if the
    /// `sync_every_ms` configuration option
    /// is set to `Some(number_of_ms_between_syncs)`
    /// or if the IO buffer gets filled to
    /// capacity before being rotated.
    pub fn was_recovered(&self) -> bool {
        self.was_recovered
    }

    /// Record a set of pages as being involved in
    /// a transaction that is about to be written to
    /// one of the structures in the Db. Do this
    /// before performing transactions in case
    /// the system crashes before all changes
    /// can be written. During recovery,
    /// you can call `possibly_dirty_pages`
    /// to get the set of pages that may have
    /// transactional state written to them.
    pub fn mark_pending_tx(
        &self,
        _pages: Vec<PageId>,
        _txid: u64,
    ) -> Result<(), ()> {
        unimplemented!()
    }

    /// Mark a transaction as being aborted. This
    /// ensures that during recovery we know that
    /// this transaction is not the last one that
    /// should be recovered, because the process
    /// finished the transaction, but we may need
    /// to clean up its pending state still.
    pub fn abort_tx(&self, _txid: u64) -> Result<(), ()> {
        unimplemented!()
    }

    /// Mark a transaction as being completed. This
    /// ensures that during recovery we know that
    /// this transaction completed, and that we
    /// can safely remove its entry in the
    /// transaction table.
    pub fn commit_tx(&self, _txid: u64) -> Result<(), ()> {
        unimplemented!()
    }

    /// Remove entries associated with this transaction
    /// in the transaction table, because we have
    /// completed the transaction and finished
    /// applying or removing pending state, and do
    /// not need to know about it during crash recovery.
    pub fn cleanup_tx(&self, _txid: u64) -> Result<(), ()> {
        unimplemented!()
    }

    /// Call this during recovery to retrieve the set of
    /// pages that were involved in a transaction
    /// that was in a pending or aborted (but not
    /// yet fully cleaned up) state during process
    /// termination.
    pub fn possibly_dirty_pages(
        &self,
    ) -> Result<Vec<(u64, Vec<PageId>)>, ()> {
        unimplemented!()
    }
}
<|endoftext|>


<commit_before>///! Utilities not directly related to parsing, lexing, or
///! language-manipulation.

/// Panic with a given message unless an expression evaluates to true.
///
/// ## Examples
/// ```rust
/// panic_unless!(1 + 1 == 2, "Math is broken.");
/// ```
macro_rules! panic_unless {
    ($condition:expr, $message:expr) => ({ if ! $condition { panic!($message); } });
}

#[macro_use]
pub mod intrusive;

#[doc(hidden)]
macro_rules! impl_methods {
    ($body_macro:ident; $(,)*) => {};
    /* args: (&mut self) */
    ($body_macro:ident; $method:ident(&mut self) -> $ret:ty, $($rest: tt)*)
    => (fn $method(&mut self) -> $ret { $body_macro!($method; mut, self, ) }
        impl_methods!($body_macro; $($rest)*););

    /* args: (&mut self, ...) */
    ($body_macro:ident; $method:ident(&mut self, $($arg:ident: $typ:ty),+) -> $ret:ty, $($rest: tt)*)
        => (fn $method(&mut self, $($arg: $typ),+) -> $ret { $body_macro!($method; mut, self, $($arg),+) }
            impl_methods!($body_macro; $($rest)*););

    /* args: (&self) */
    ($body_macro:ident; $method:ident(&self) -> $ret:ty, $($rest: tt)*)
        => (fn $method(&self) -> $ret { $body_macro!($method; , self, ) }
            impl_methods!($body_macro; $($rest)*););

    /* args: (&self, ...) */
    ($body_macro:ident; $method:ident(&self, $($arg:ident: $typ:ty),+) -> $ret:ty, $($rest: tt)*)
        => (fn $method(&mut self, $($arg: $typ),+) -> $ret { $body_macro!($method; , self, $($arg),+) }
            impl_methods!($body_macro; $($rest)*););
}

#[doc(hidden)]
#[macro_export]
macro_rules! impl_for {
    ($tr:path, $what:path, $body_macro:ident, $($rest: tt)*) => (impl $tr for $what { impl_methods!($body_macro; $($rest)*); });
}
<commit_msg>mod util: export `panic_unless`, allow fmt args on same<commit_after>///! Utilities not directly related to parsing, lexing, or
///! language-manipulation.

/// Panic with a given message unless an expression evaluates to true.
///
/// ## Examples
/// ```should_fail
/// panic_unless!(1 + 1 == 2, "Math is broken.");
/// ```
#[macro_export]
macro_rules! panic_unless {
    ($condition:expr, $($rest:expr),+) => ({ if ! $condition { panic!($($rest),+); } });
}

#[macro_use]
pub mod intrusive;

#[doc(hidden)]
macro_rules! impl_methods {
    ($body_macro:ident; $(,)*) => {};
    /* args: (&mut self) */
    ($body_macro:ident; $method:ident(&mut self) -> $ret:ty, $($rest: tt)*)
    => (fn $method(&mut self) -> $ret { $body_macro!($method; mut, self, ) }
        impl_methods!($body_macro; $($rest)*););

    /* args: (&mut self, ...) */
    ($body_macro:ident; $method:ident(&mut self, $($arg:ident: $typ:ty),+) -> $ret:ty, $($rest: tt)*)
        => (fn $method(&mut self, $($arg: $typ),+) -> $ret { $body_macro!($method; mut, self, $($arg),+) }
            impl_methods!($body_macro; $($rest)*););

    /* args: (&self) */
    ($body_macro:ident; $method:ident(&self) -> $ret:ty, $($rest: tt)*)
        => (fn $method(&self) -> $ret { $body_macro!($method; , self, ) }
            impl_methods!($body_macro; $($rest)*););

    /* args: (&self, ...) */
    ($body_macro:ident; $method:ident(&self, $($arg:ident: $typ:ty),+) -> $ret:ty, $($rest: tt)*)
        => (fn $method(&mut self, $($arg: $typ),+) -> $ret { $body_macro!($method; , self, $($arg),+) }
            impl_methods!($body_macro; $($rest)*););
}

#[doc(hidden)]
#[macro_export]
macro_rules! impl_for {
    ($tr:path, $what:path, $body_macro:ident, $($rest: tt)*) => (impl $tr for $what { impl_methods!($body_macro; $($rest)*); });
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

mod board;
mod sys;

use imgui::{self, ImVec2, Ui};
use imgui_sys;

use model::{Color, FieldCoord, Model};
use self::board::board;
pub use self::sys::run;

pub enum Event {
    Click(FieldCoord),
}

pub fn draw(ui: &Ui, size: (f32, f32), model: &Model) -> Option<Event> {
    unsafe {
        imgui_sys::igPushStyleVar(imgui_sys::ImGuiStyleVar::WindowRounding, 0.0);
    }

    let mut event = None;
    ui.window(im_str!("Coerceo"))
        .size(size, imgui::ImGuiSetCond_Always)
        .position((0.0, 0.0), imgui::ImGuiSetCond_Once)
        .title_bar(false)
        .resizable(false)
        .movable(false)
        .build(|| {
            ui.text(im_str!("Welcome to Coerceo!"));

            event = board(model, &ImVec2::new(size.0 - 16.0, size.1 - 90.0));

            if model.white_pieces == 0 {
                ui.text(im_str!("Black wins!"));
            } else if model.black_pieces == 0 {
                ui.text(im_str!("White wins!"));
            } else {
                match model.turn {
                    Color::White => ui.text(im_str!("It's white's turn.")),
                    Color::Black => ui.text(im_str!("It's black's turn.")),
                }

                ui.text(im_str!(
                    "White has {} piece(s) left and {} captured hex(es).",
                    model.white_pieces,
                    model.white_hexes
                ));
                ui.text(im_str!(
                    "Black has {} piece(s) left and {} captured hex(es).",
                    model.black_pieces,
                    model.black_hexes
                ));
            }
        });

    unsafe {
        imgui_sys::igPopStyleVar(1);
    }

    event
}
<commit_msg>Add skeleton menu<commit_after>/*
 * Copyright (C) 2017 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

mod board;
mod sys;

use imgui::{self, ImVec2, Ui};
use imgui_sys;

use model::{Color, FieldCoord, Model};
use self::board::board;
pub use self::sys::run;

pub enum Event {
    Click(FieldCoord),
}

pub fn draw(ui: &Ui, size: (f32, f32), model: &Model) -> Option<Event> {
    unsafe {
        imgui_sys::igPushStyleVar(imgui_sys::ImGuiStyleVar::WindowRounding, 0.0);
    }

    let mut event = None;

    ui.main_menu_bar(|| {
        ui.menu(im_str!("Game"))
            .build(|| if ui.menu_item(im_str!("New game")).build() {
                println!("Menu item clicked");
            });
    });

    ui.window(im_str!("Coerceo"))
        .size(size, imgui::ImGuiSetCond_Always)
        .position((0.0, 19.0), imgui::ImGuiSetCond_Once)
        .title_bar(false)
        .resizable(false)
        .movable(false)
        .build(|| {
            ui.text(im_str!("Welcome to Coerceo!"));

            event = board(model, &ImVec2::new(size.0 - 16.0, size.1 - 100.0));

            if model.white_pieces == 0 {
                ui.text(im_str!("Black wins!"));
            } else if model.black_pieces == 0 {
                ui.text(im_str!("White wins!"));
            } else {
                match model.turn {
                    Color::White => ui.text(im_str!("It's white's turn.")),
                    Color::Black => ui.text(im_str!("It's black's turn.")),
                }

                ui.text(im_str!(
                    "White has {} piece(s) left and {} captured hex(es).",
                    model.white_pieces,
                    model.white_hexes
                ));
                ui.text(im_str!(
                    "Black has {} piece(s) left and {} captured hex(es).",
                    model.black_pieces,
                    model.black_hexes
                ));
            }
        });

    unsafe {
        imgui_sys::igPopStyleVar(1);
    }

    event
}
<|endoftext|>


<commit_before>//Copyright (c) 2016, Ruslan Baratov, Alex Frappier Lachapelle
//All rights reserved.

use std::collections::BTreeMap;
use std::error::Error;

extern crate iron;
use self::iron::prelude::*;
use self::iron::status;

extern crate rand;
use self::rand::Rng;

extern crate serde;
extern crate serde_json;

use config;
use utilities;

////////////////////////////////////////////////////////////
//                          Funcs                         //
////////////////////////////////////////////////////////////

pub fn register(config: &mut config::ConfigHandler) {

    //List of events to listent for.
    let hooks = vec!["issue_comment","pull_request_review_comment"];

    println!("Setting up webhooks...");

    //Create a random number for the "secret"
    //which will be used for verifying that
    //github is the actual sender of the webhook
    //via SHA1 HMAC

    //Skip secret number generation if we already made one before
    //Get webhook secret
    let mut github_webhook_secret = String::new();
    match config.get_string("state", "github_webhook_secret") {
        Ok(_github_webhook_secret) => github_webhook_secret = _github_webhook_secret,
        Err(err)                   => {panic!("Error getting  the \"github_webhook_secret\" value from config: {}", err);}
    }

    if github_webhook_secret == String::new() {
        // Get the RNG
        let mut rng = match rand::os::OsRng::new() {
            Ok(_rng) => _rng,
            Err(err) => panic!("Failed to obtain OS RNG: {}", err)
        };

        let github_webhook_secret = rng.next_u64().to_string();
        config.set_string("state", "github_webhook_secret", &github_webhook_secret[..]);
        match config.save() {
            Ok(()) => (),
            Err(_) => {panic!("Failed to save the config file.");}
        }
    }

    //Get follow repo
    let mut github_follow_repo = String::new();
    match config.get_string("config", "github_follow_repo") {
        Ok(_github_follow_repo) => github_follow_repo = _github_follow_repo,
        Err(err)          => {panic!("Error getting  the \"github_follow_repo\" value from config: {}", err);}
    }

    //Get owner api token
    let mut github_owner_token = String::new();
    match config.get_string("config", "github_owner_token") {
        Ok(_github_owner_token) => github_owner_token = _github_owner_token,
        Err(err)          => {panic!("Error getting  the \"github_owner_token\" value from config: {}", err);}
    }

    //Create JSON data
    let mut json_data:        BTreeMap<String, serde_json::Value> = BTreeMap::new();
    let mut json_data_config: BTreeMap<String, serde_json::Value> = BTreeMap::new();
    let mut json_data_events: Vec<serde_json::Value>              = Vec::new();

    let mut self_address = String::new();
    match config.get_string("config", "self_address") {
        Ok(_self_address) => self_address = _self_address,
        Err(err)          => {panic!("Error getting  the \"self_address\" value from config: {}", err);}
    }

    let mut listen_port = String::new();
    match config.get_string("config", "listen_port") {
        Ok(_listen_port) => listen_port = _listen_port,
        Err(err)          => {panic!("Error getting  the \"listen_port\" value from config: {}", err);}
    }

    self_address.push_str(":");
    self_address.push_str(&listen_port[..]);
    self_address.push_str("/webhook");

    json_data.insert(String::from("name"),                serde_json::Value::String(String::from("web")));
    json_data.insert(String::from("active"),              serde_json::Value::Bool(true));
    json_data_config.insert(String::from("url"),          serde_json::Value::String(self_address));
    json_data_config.insert(String::from("content_type"), serde_json::Value::String(String::from("json")));
    json_data_config.insert(String::from("secret"),       serde_json::Value::String(github_webhook_secret));
    json_data_config.insert(String::from("insecure_ssl"), serde_json::Value::String(String::from("1")));

    for hook in &hooks {
        json_data_events.push(serde_json::Value::String(hook.to_string()));
    }

    json_data.insert(String::from("config"), serde_json::Value::Object(json_data_config));
    json_data.insert(String::from("events"), serde_json::Value::Array(json_data_events));

    let mut json_data_string = String::new();
    match serde_json::to_string(&json_data) {
        Ok(_json_data_string) => json_data_string = _json_data_string,
        Err(err)              => {panic!("Faild to create JSON data to initialize webhooks: {}", err.description());}
    }

    //Register webhooks
    let endpoint = format!("repos/{}/hooks?access_token={}", github_follow_repo, github_owner_token);
    match utilities::github_post_request(endpoint, json_data_string) {
        Ok(())   => (),
        Err(err) => {panic!("Failed to register webhooks: {}", err)}
    }

    println!("Success!")
}

pub fn testreceiver(request: &mut Request) -> IronResult<Response> {
    println!("-----------------------------------------------------------");
    println!("url:         {:?}", request.url);
    println!("remote_addr: {:?}", request.remote_addr);
    println!("local_addr:  {:?}", request.local_addr);
    println!("headers:     {:?}", request.headers);
    //println!("body:        {:?}", request.body); ///////////////////////
    println!("method:      {:?}", request.method);
    //println!("extensions:  {:?}", request.extensions);
    Ok(Response::with((status::Ok, "Hello World!")))
}

pub fn listen(config: &mut config::ConfigHandler) {

    //Start server thread
    Iron::new(testreceiver).http("0.0.0.0:3000").unwrap();
    //Process events

}

<commit_msg>Fixed webhook secret generation<commit_after>//Copyright (c) 2016, Ruslan Baratov, Alex Frappier Lachapelle
//All rights reserved.

use std::collections::BTreeMap;
use std::error::Error;

extern crate iron;
use self::iron::prelude::*;
use self::iron::status;

extern crate rand;
use self::rand::Rng;

extern crate serde;
extern crate serde_json;

use config;
use utilities;

////////////////////////////////////////////////////////////
//                          Funcs                         //
////////////////////////////////////////////////////////////

pub fn register(config: &mut config::ConfigHandler) {

    //List of events to listent for.
    let hooks = vec!["issue_comment","pull_request_review_comment"];

    println!("Setting up webhooks...");

    //Create a random number for the "secret"
    //which will be used for verifying that
    //github is the actual sender of the webhook
    //via SHA1 HMAC

    //Skip secret number generation if we already made one before
    //Get webhook secret
    let mut github_webhook_secret = String::new();
    match config.get_string("state", "github_webhook_secret") {
        Ok(_github_webhook_secret) => github_webhook_secret = _github_webhook_secret,
        Err(err)                   => {panic!("Error getting  the \"github_webhook_secret\" value from config: {}", err);}
    }

    if github_webhook_secret == String::new() {
        // Get the RNG
        let mut rng = match rand::os::OsRng::new() {
            Ok(_rng) => _rng,
            Err(err) => panic!("Failed to obtain OS RNG: {}", err)
        };

        github_webhook_secret = rng.next_u64().to_string();
        config.set_string("state", "github_webhook_secret", &github_webhook_secret.clone()[..]);
        match config.save() {
            Ok(()) => (),
            Err(_) => {panic!("Failed to save the config file.");}
        }
    }

    //Get follow repo
    let mut github_follow_repo = String::new();
    match config.get_string("config", "github_follow_repo") {
        Ok(_github_follow_repo) => github_follow_repo = _github_follow_repo,
        Err(err)          => {panic!("Error getting  the \"github_follow_repo\" value from config: {}", err);}
    }

    //Get owner api token
    let mut github_owner_token = String::new();
    match config.get_string("config", "github_owner_token") {
        Ok(_github_owner_token) => github_owner_token = _github_owner_token,
        Err(err)          => {panic!("Error getting  the \"github_owner_token\" value from config: {}", err);}
    }

    //Create JSON data
    let mut json_data:        BTreeMap<String, serde_json::Value> = BTreeMap::new();
    let mut json_data_config: BTreeMap<String, serde_json::Value> = BTreeMap::new();
    let mut json_data_events: Vec<serde_json::Value>              = Vec::new();

    let mut self_address = String::new();
    match config.get_string("config", "self_address") {
        Ok(_self_address) => self_address = _self_address,
        Err(err)          => {panic!("Error getting  the \"self_address\" value from config: {}", err);}
    }

    let mut listen_port = String::new();
    match config.get_string("config", "listen_port") {
        Ok(_listen_port) => listen_port = _listen_port,
        Err(err)          => {panic!("Error getting  the \"listen_port\" value from config: {}", err);}
    }

    self_address.push_str(":");
    self_address.push_str(&listen_port[..]);
    self_address.push_str("/webhook");

    json_data.insert(String::from("name"),                serde_json::Value::String(String::from("web")));
    json_data.insert(String::from("active"),              serde_json::Value::Bool(true));
    json_data_config.insert(String::from("url"),          serde_json::Value::String(self_address));
    json_data_config.insert(String::from("content_type"), serde_json::Value::String(String::from("json")));
    json_data_config.insert(String::from("secret"),       serde_json::Value::String(github_webhook_secret));
    json_data_config.insert(String::from("insecure_ssl"), serde_json::Value::String(String::from("1")));

    for hook in &hooks {
        json_data_events.push(serde_json::Value::String(hook.to_string()));
    }

    json_data.insert(String::from("config"), serde_json::Value::Object(json_data_config));
    json_data.insert(String::from("events"), serde_json::Value::Array(json_data_events));

    let mut json_data_string = String::new();
    match serde_json::to_string(&json_data) {
        Ok(_json_data_string) => json_data_string = _json_data_string,
        Err(err)              => {panic!("Faild to create JSON data to initialize webhooks: {}", err.description());}
    }

    //Register webhooks
    let endpoint = format!("repos/{}/hooks?access_token={}", github_follow_repo, github_owner_token);
    match utilities::github_post_request(endpoint, json_data_string) {
        Ok(())   => (),
        Err(err) => {panic!("Failed to register webhooks: {}", err)}
    }

    println!("Success!")
}

pub fn testreceiver(request: &mut Request) -> IronResult<Response> {
    println!("-----------------------------------------------------------");
    println!("url:         {:?}", request.url);
    println!("remote_addr: {:?}", request.remote_addr);
    println!("local_addr:  {:?}", request.local_addr);
    println!("headers:     {:?}", request.headers);
    //println!("body:        {:?}", request.body); ///////////////////////
    println!("method:      {:?}", request.method);
    //println!("extensions:  {:?}", request.extensions);
    Ok(Response::with((status::Ok, "Hello World!")))
}

pub fn listen(config: &mut config::ConfigHandler) {

    //Start server thread
    Iron::new(testreceiver).http("0.0.0.0:3000").unwrap();
    //Process events

}

<|endoftext|>


<commit_before>extern crate graph_match;
use std::collections::HashMap;

pub fn parse(string: String) -> Result<graph_match::graph::Graph, String> {
    let mut graph = graph_match::graph::Graph { nodes: vec![], edges: vec![] };
    for line in string.split("\n") {
        let attributes = parse_line(line.to_string());
        match attributes.get("type") {
            Some(element_type) => {
                match element_type.as_str() {
                    "node" => {
                        match attributes.get("identifier") {
                            Some(identifier) => {
                                graph.add_node(identifier.clone(), Some(attributes.clone()));
                            },
                            None => return Err("Missing identifier".to_string())
                        }
                    },
                    "edge" => {
                        match attributes.get("identifier") {
                            Some(identifier) => {
                                match extract_source_target(&attributes) {
                                    Some((source, target)) => {
                                        graph.add_edge(source, target, identifier.clone(), Some(attributes.clone()));
                                    },
                                    None => return Err("Invalid source or target".to_string())
                                }
                            },
                            None => return Err("Missing identifier".to_string())
                        }
                    },
                    _ => { return Err("Invalid type".to_string()) },
                }
            }
            None => return Err("A line must have a graph component type".to_string())
        }
    }
    return Ok(graph);
}

fn parse_line(line: String) -> HashMap<String,String> {
    let mut attributes: HashMap<String,String> = HashMap::new();
    for pair in line.split(" ") {
        let mut pair = pair.split(":");
        match pair.next() {
            Some(k) => {
                match pair.next() {
                    Some(v) => {
                        attributes.insert(k.to_string(), v.to_string());
                    },
                    None => continue,
                }
            },
            None => continue,
        }
    }
    return attributes;
}

fn extract_source_target(attributes: &HashMap<String,String>) -> Option<(usize,usize)> {
    match attributes.get("source") {
        Some(source) => {
            match attributes.get("target") {
                Some(target) => {
                    let usize_source = source.parse::<usize>();
                    let usize_target = target.parse::<usize>();
                    if usize_source.is_ok() && usize_target.is_ok() {
                        return Some((usize_source.unwrap(), usize_target.unwrap()));
                    } else {
                        return None;
                    }
                },
                None => return None,
            }
        },
        None => return None,
    }
}

<commit_msg>Add test cases for the graph parsing<commit_after>extern crate graph_match;
use std::collections::HashMap;

pub fn parse(string: String) -> Result<graph_match::graph::Graph, String> {
    let mut graph = graph_match::graph::Graph { nodes: vec![], edges: vec![] };
    for line in string.split("\n") {
        let attributes = parse_line(line.to_string());
        match attributes.get("type") {
            Some(element_type) => {
                match element_type.as_str() {
                    "node" => {
                        match attributes.get("identifier") {
                            Some(identifier) => {
                                graph.add_node(identifier.clone(), Some(attributes.clone()));
                            },
                            None => return Err("Missing identifier".to_string())
                        }
                    },
                    "edge" => {
                        match attributes.get("identifier") {
                            Some(identifier) => {
                                match extract_source_target(&attributes) {
                                    Some((source, target)) => {
                                        graph.add_edge(source, target, identifier.clone(), Some(attributes.clone()));
                                    },
                                    None => return Err("Invalid source or target".to_string())
                                }
                            },
                            None => return Err("Missing identifier".to_string())
                        }
                    },
                    _ => { return Err("Invalid type".to_string()) },
                }
            }
            None => return Err("A line must have a graph component type".to_string())
        }
    }
    return Ok(graph);
}

#[test]
fn parse_valid_graph() {
    let string_graph =
        "type:node identifier:node0\n\
         type:node identifier:node1\n\
         type:edge identifier:edge1 source:0 target:1";
    let graph = parse(string_graph.to_string());
    assert_eq!(true, graph.is_ok());
}
#[test]
fn parse_invalid_graph() {
    let string_graph =
        "type:thing identifier:node0\n\
         type:eddge identifier:edge1 source:0 target:1";
    let graph = parse(string_graph.to_string());
    assert_eq!(false, graph.is_ok());
}

fn parse_line(line: String) -> HashMap<String,String> {
    let mut attributes: HashMap<String,String> = HashMap::new();
    for pair in line.split(" ") {
        let mut pair = pair.split(":");
        match pair.next() {
            Some(k) => {
                if k.len() == 0 { continue; }
                match pair.next() {
                    Some(v) => {
                        if v.len() == 0 { continue; }
                        attributes.insert(k.to_string(), v.to_string());
                    },
                    None => continue,
                }
            },
            None => continue,
        }
    }
    return attributes;
}

#[test]
fn parse_line_to_attributes() {
    let line = "type:node key:value".to_string();
    let attributes = parse_line(line);
    let mut expected_attributes: HashMap<String,String> = HashMap::new();
    expected_attributes.insert("type".to_string(), "node".to_string());
    expected_attributes.insert("key".to_string(), "value".to_string());
    assert_eq!(expected_attributes.len(), attributes.len());
    for pair in &expected_attributes {
        assert_eq!(attributes.get(pair.0).unwrap(), pair.1);
    }
}
#[test]
fn parse_line_to_attributes_poorly_formatted() {
    let line = "type:node key:value key2 : value ".to_string();
    let attributes = parse_line(line);
    let mut expected_attributes: HashMap<String,String> = HashMap::new();
    expected_attributes.insert("type".to_string(), "node".to_string());
    expected_attributes.insert("key".to_string(), "value".to_string());
    assert_eq!(expected_attributes.len(), attributes.len());
    for pair in &expected_attributes {
        assert_eq!(attributes.get(pair.0).unwrap(), pair.1);
    }
}

fn extract_source_target(attributes: &HashMap<String,String>) -> Option<(usize,usize)> {
    match attributes.get("source") {
        Some(source) => {
            match attributes.get("target") {
                Some(target) => {
                    let usize_source = source.parse::<usize>();
                    let usize_target = target.parse::<usize>();
                    if usize_source.is_ok() && usize_target.is_ok() {
                        return Some((usize_source.unwrap(), usize_target.unwrap()));
                    } else {
                        return None;
                    }
                },
                None => return None,
            }
        },
        None => return None,
    }
}

#[test]
fn extract_source_and_target_from_attributes() {
    let mut attributes: HashMap<String,String> = HashMap::new();
    attributes.insert("source".to_string(), "1".to_string());
    attributes.insert("target".to_string(), "2".to_string());
    assert_eq!(Some((1,2)), extract_source_target(&attributes));
}
#[test]
fn extract_source_and_target_from_attributes_invalid() {
    let mut attributes: HashMap<String,String> = HashMap::new();
    attributes.insert("source".to_string(), "a".to_string());
    attributes.insert("target".to_string(), "b".to_string());
    assert_eq!(None, extract_source_target(&attributes));
}
#[test]
fn extract_source_and_target_from_attributes_missing() {
    let mut attributes: HashMap<String,String> = HashMap::new();
    attributes.insert("key".to_string(), "value".to_string());
    assert_eq!(None, extract_source_target(&attributes));
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::sync;

use error::{Error, Result};
use std::borrow;
use value::{Index, Object, Value};

use super::Argument;
use super::Globals;
use super::{BoxedValueFilter, FilterValue};

/// Format an error for an unexpected value.
pub fn unexpected_value_error<S: ToString>(expected: &str, actual: Option<S>) -> Error {
    let actual = actual.map(|x| x.to_string());
    unexpected_value_error_string(expected, actual)
}

fn unexpected_value_error_string(expected: &str, actual: Option<String>) -> Error {
    let actual = actual.unwrap_or_else(|| "nothing".to_owned());
    Error::with_msg(format!("Expected {}, found `{}`", expected, actual))
}

/// Block processing interrupt state.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Interrupt {
    /// Restart processing the current block.
    Continue,
    /// Stop processing the current block.
    Break,
}

/// The current interrupt state. The interrupt state is used by
/// the `break` and `continue` tags to halt template rendering
/// at a given point and unwind the `render` call stack until
/// it reaches an enclosing `for_loop`. At that point the interrupt
/// is cleared, and the `for_loop` carries on processing as directed.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct InterruptState {
    interrupt: Option<Interrupt>,
}

impl InterruptState {
    /// An interrupt state is active.
    pub fn interrupted(&self) -> bool {
        self.interrupt.is_some()
    }

    /// Sets the interrupt state. Any previous state is obliterated.
    pub fn set_interrupt(&mut self, interrupt: Interrupt) {
        self.interrupt = Some(interrupt);
    }

    /// Fetches and clears the interrupt state.
    pub fn pop_interrupt(&mut self) -> Option<Interrupt> {
        let rval = self.interrupt;
        self.interrupt = None;
        rval
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
struct CycleStateInner {
    // The indices of all the cycles encountered during rendering.
    cycles: HashMap<String, usize>,
}

impl CycleStateInner {
    fn cycle_index(&mut self, name: &str, max: usize) -> usize {
        let i = self.cycles.entry(name.to_owned()).or_insert(0);
        let j = *i;
        *i = (*i + 1) % max;
        j
    }
}

/// See `cycle` tag.
pub struct CycleState<'a, 'g>
where
    'g: 'a,
{
    context: &'a mut Context<'g>,
}

impl<'a, 'g> CycleState<'a, 'g> {
    /// See `cycle` tag.
    pub fn cycle_element(&mut self, name: &str, values: &[Argument]) -> Result<Value> {
        let index = self.context.cycles.cycle_index(name, values.len());
        if index >= values.len() {
            return Err(Error::with_msg(
                "cycle index out of bounds, most likely from mismatched cycles",
            ).context("index", format!("{}", index))
                .context("count", format!("{}", values.len())));
        }

        let val = values[index].evaluate(self.context)?;
        Ok(val)
    }
}

/// Stack of variables.
#[derive(Debug, Clone)]
pub struct Stack<'g> {
    globals: Option<&'g Globals>,
    stack: Vec<Object>,
}

impl<'g> Stack<'g> {
    /// Create an empty stack
    pub fn empty() -> Self {
        Self {
            globals: None,
            // Mutable frame for globals.
            stack: vec![Object::new()],
        }
    }

    /// Create a stack initialized with read-only `Globals`.
    pub fn with_globals(globals: &'g Globals) -> Self {
        let mut stack = Self::empty();
        stack.globals = Some(globals);
        stack
    }

    /// Creates a new variable scope chained to a parent scope.
    fn push_frame(&mut self) {
        self.stack.push(Object::new());
    }

    /// Removes the topmost stack frame from the local variable stack.
    ///
    /// # Panics
    ///
    /// This method will panic if popping the topmost frame results in an
    /// empty stack. Given that a context is created with a top-level stack
    /// frame already in place, empyting the stack should never happen in a
    /// well-formed program.
    fn pop_frame(&mut self) {
        if self.stack.pop().is_none() {
            panic!("Pop leaves empty stack")
        };
    }

    /// Recursively index into the stack.
    pub fn get_val_by_index<'i, I: Iterator<Item = &'i Index>>(
        &self,
        mut indexes: I,
    ) -> Result<&Value> {
        let key = indexes
            .next()
            .ok_or_else(|| Error::with_msg("No index provided"))?;
        let key = key.as_key().ok_or_else(|| {
            Error::with_msg("Root index must be an object key").context("index", format!("{}", key))
        })?;
        let value = self
            .get_val(key)
            .ok_or_else(|| Error::with_msg("Invalid index").context("index", key.to_owned()))?;

        indexes.fold(Ok(value), |value, index| {
            let value = value?;
            let child = value.get(index);
            let child = child
                .ok_or_else(|| Error::with_msg("Invalid index").context("index", key.to_owned()))?;
            Ok(child)
        })
    }

    fn get_val<'a>(&'a self, name: &str) -> Option<&'a Value> {
        for frame in self.stack.iter().rev() {
            if let rval @ Some(_) = frame.get(name) {
                return rval;
            }
        }
        self.globals.and_then(|g| g.get(name))
    }

    /// Sets a value in the global context.
    pub fn set_global_val<S>(&mut self, name: S, val: Value) -> Option<Value>
    where
        S: Into<borrow::Cow<'static, str>>,
    {
        self.global_frame().insert(name.into(), val)
    }

    /// Sets a value to the rendering context.
    /// Note that it needs to be wrapped in a liquid::Value.
    ///
    /// # Panics
    ///
    /// Panics if there is no frame on the local values stack. Context
    /// instances are created with a top-level stack frame in place, so
    /// this should never happen in a well-formed program.
    pub fn set_val<S>(&mut self, name: S, val: Value) -> Option<Value>
    where
        S: Into<borrow::Cow<'static, str>>,
    {
        self.current_frame().insert(name.into(), val)
    }

    fn current_frame(&mut self) -> &mut Object {
        match self.stack.last_mut() {
            Some(frame) => frame,
            None => panic!("Global frame removed."),
        }
    }

    fn global_frame(&mut self) -> &mut Object {
        match self.stack.first_mut() {
            Some(frame) => frame,
            None => panic!("Global frame removed."),
        }
    }
}

impl<'g> Default for Stack<'g> {
    fn default() -> Self {
        Self::empty()
    }
}

/// Create processing context for a template.
pub struct ContextBuilder<'g> {
    globals: Option<&'g Globals>,
    filters: sync::Arc<HashMap<&'static str, BoxedValueFilter>>,
}

impl<'g> ContextBuilder<'g> {
    /// Creates a new, empty rendering context.
    pub fn new() -> Self {
        Self {
            globals: None,
            filters: Default::default(),
        }
    }

    /// Initialize the stack with the given globals.
    pub fn set_globals(mut self, values: &'g Globals) -> Self {
        self.globals = Some(values);
        self
    }

    /// Initialize the context with the given filters.
    pub fn set_filters(
        mut self,
        filters: &sync::Arc<HashMap<&'static str, BoxedValueFilter>>,
    ) -> Self {
        self.filters = sync::Arc::clone(filters);
        self
    }

    /// Create the `Context`.
    pub fn build(self) -> Context<'g> {
        let stack = match self.globals {
            Some(globals) => Stack::with_globals(globals),
            None => Stack::empty(),
        };
        Context {
            stack,
            interrupt: InterruptState::default(),
            cycles: CycleStateInner::default(),
            filters: self.filters,
        }
    }
}

impl<'g> Default for ContextBuilder<'g> {
    fn default() -> Self {
        Self::new()
    }
}

/// Processing context for a template.
#[derive(Default)]
pub struct Context<'g> {
    stack: Stack<'g>,

    interrupt: InterruptState,
    cycles: CycleStateInner,

    filters: sync::Arc<HashMap<&'static str, BoxedValueFilter>>,
}

impl<'g> Context<'g> {
    /// Create a default `Context`.
    ///
    /// See `ContextBuilder` for more control.
    pub fn new() -> Self {
        Context::default()
    }

    /// Grab a `FilterValue`.
    pub fn get_filter<'b>(&'b self, name: &str) -> Option<&'b FilterValue> {
        self.filters.get(name).map(|f| {
            let f: &FilterValue = f;
            f
        })
    }

    /// Access the block's `InterruptState`.
    pub fn interrupt(&self) -> &InterruptState {
        &self.interrupt
    }

    /// Access the block's `InterruptState`.
    pub fn interrupt_mut(&mut self) -> &mut InterruptState {
        &mut self.interrupt
    }

    /// See `cycle` tag.
    pub fn cycles<'a>(&'a mut self) -> CycleState<'a, 'g>
    where
        'g: 'a,
    {
        CycleState { context: self }
    }

    /// Access the current `Stack`.
    pub fn stack(&self) -> &Stack {
        &self.stack
    }

    /// Access the current `Stack`.
    pub fn stack_mut<'a>(&'a mut self) -> &'a mut Stack<'g>
    where
        'g: 'a,
    {
        &mut self.stack
    }

    /// Sets up a new stack frame, executes the supplied function and then
    /// tears the stack frame down before returning the function's result
    /// to the caller.
    pub fn run_in_scope<RvalT, FnT>(&mut self, f: FnT) -> RvalT
    where
        FnT: FnOnce(&mut Context) -> RvalT,
    {
        self.stack.push_frame();
        let result = f(self);
        self.stack.pop_frame();
        result
    }
}

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

    #[test]
    fn get_val() {
        let mut ctx = Context::new();
        ctx.stack_mut()
            .set_global_val("number", Value::scalar(42f64));
        assert_eq!(
            ctx.stack().get_val("number").unwrap(),
            &Value::scalar(42f64)
        );
    }

    #[test]
    fn get_val_failure() {
        let mut ctx = Context::new();
        let mut post = Object::new();
        post.insert("number".into(), Value::scalar(42f64));
        ctx.stack_mut().set_global_val("post", Value::Object(post));
        assert!(ctx.stack().get_val("post.number").is_none());
    }

    #[test]
    fn get_val_by_index() {
        let mut ctx = Context::new();
        let mut post = Object::new();
        post.insert("number".into(), Value::scalar(42f64));
        ctx.stack_mut().set_global_val("post", Value::Object(post));
        let indexes = vec![Index::with_key("post"), Index::with_key("number")];
        assert_eq!(
            ctx.stack().get_val_by_index(indexes.iter()).unwrap(),
            &Value::scalar(42f64)
        );
    }

    #[test]
    fn scoped_variables() {
        let mut ctx = Context::new();
        ctx.stack_mut().set_global_val("test", Value::scalar(42f64));
        assert_eq!(ctx.stack().get_val("test").unwrap(), &Value::scalar(42f64));

        ctx.run_in_scope(|new_scope| {
            // assert that values are chained to the parent scope
            assert_eq!(
                new_scope.stack().get_val("test").unwrap(),
                &Value::scalar(42f64)
            );

            // set a new local value, and assert that it overrides the previous value
            new_scope
                .stack_mut()
                .set_val("test", Value::scalar(3.14f64));
            assert_eq!(
                new_scope.stack().get_val("test").unwrap(),
                &Value::scalar(3.14f64)
            );

            // sat a new val that we will pick up outside the scope
            new_scope
                .stack_mut()
                .set_global_val("global", Value::scalar("some value"));
        });

        // assert that the value has reverted to the old one
        assert_eq!(ctx.stack().get_val("test").unwrap(), &Value::scalar(42f64));
        assert_eq!(
            ctx.stack().get_val("global").unwrap(),
            &Value::scalar("some value")
        );
    }
}
<commit_msg>chore: Style<commit_after>use std::borrow;
use std::collections::HashMap;
use std::sync;

use error::{Error, Result};
use value::{Index, Object, Value};

use super::Argument;
use super::Globals;
use super::{BoxedValueFilter, FilterValue};

/// Format an error for an unexpected value.
pub fn unexpected_value_error<S: ToString>(expected: &str, actual: Option<S>) -> Error {
    let actual = actual.map(|x| x.to_string());
    unexpected_value_error_string(expected, actual)
}

fn unexpected_value_error_string(expected: &str, actual: Option<String>) -> Error {
    let actual = actual.unwrap_or_else(|| "nothing".to_owned());
    Error::with_msg(format!("Expected {}, found `{}`", expected, actual))
}

/// Block processing interrupt state.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Interrupt {
    /// Restart processing the current block.
    Continue,
    /// Stop processing the current block.
    Break,
}

/// The current interrupt state. The interrupt state is used by
/// the `break` and `continue` tags to halt template rendering
/// at a given point and unwind the `render` call stack until
/// it reaches an enclosing `for_loop`. At that point the interrupt
/// is cleared, and the `for_loop` carries on processing as directed.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct InterruptState {
    interrupt: Option<Interrupt>,
}

impl InterruptState {
    /// An interrupt state is active.
    pub fn interrupted(&self) -> bool {
        self.interrupt.is_some()
    }

    /// Sets the interrupt state. Any previous state is obliterated.
    pub fn set_interrupt(&mut self, interrupt: Interrupt) {
        self.interrupt = Some(interrupt);
    }

    /// Fetches and clears the interrupt state.
    pub fn pop_interrupt(&mut self) -> Option<Interrupt> {
        let rval = self.interrupt;
        self.interrupt = None;
        rval
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Default)]
struct CycleStateInner {
    // The indices of all the cycles encountered during rendering.
    cycles: HashMap<String, usize>,
}

impl CycleStateInner {
    fn cycle_index(&mut self, name: &str, max: usize) -> usize {
        let i = self.cycles.entry(name.to_owned()).or_insert(0);
        let j = *i;
        *i = (*i + 1) % max;
        j
    }
}

/// See `cycle` tag.
pub struct CycleState<'a, 'g>
where
    'g: 'a,
{
    context: &'a mut Context<'g>,
}

impl<'a, 'g> CycleState<'a, 'g> {
    /// See `cycle` tag.
    pub fn cycle_element(&mut self, name: &str, values: &[Argument]) -> Result<Value> {
        let index = self.context.cycles.cycle_index(name, values.len());
        if index >= values.len() {
            return Err(Error::with_msg(
                "cycle index out of bounds, most likely from mismatched cycles",
            ).context("index", format!("{}", index))
                .context("count", format!("{}", values.len())));
        }

        let val = values[index].evaluate(self.context)?;
        Ok(val)
    }
}

/// Stack of variables.
#[derive(Debug, Clone)]
pub struct Stack<'g> {
    globals: Option<&'g Globals>,
    stack: Vec<Object>,
}

impl<'g> Stack<'g> {
    /// Create an empty stack
    pub fn empty() -> Self {
        Self {
            globals: None,
            // Mutable frame for globals.
            stack: vec![Object::new()],
        }
    }

    /// Create a stack initialized with read-only `Globals`.
    pub fn with_globals(globals: &'g Globals) -> Self {
        let mut stack = Self::empty();
        stack.globals = Some(globals);
        stack
    }

    /// Creates a new variable scope chained to a parent scope.
    fn push_frame(&mut self) {
        self.stack.push(Object::new());
    }

    /// Removes the topmost stack frame from the local variable stack.
    ///
    /// # Panics
    ///
    /// This method will panic if popping the topmost frame results in an
    /// empty stack. Given that a context is created with a top-level stack
    /// frame already in place, empyting the stack should never happen in a
    /// well-formed program.
    fn pop_frame(&mut self) {
        if self.stack.pop().is_none() {
            panic!("Pop leaves empty stack")
        };
    }

    /// Recursively index into the stack.
    pub fn get_val_by_index<'i, I: Iterator<Item = &'i Index>>(
        &self,
        mut indexes: I,
    ) -> Result<&Value> {
        let key = indexes
            .next()
            .ok_or_else(|| Error::with_msg("No index provided"))?;
        let key = key.as_key().ok_or_else(|| {
            Error::with_msg("Root index must be an object key").context("index", format!("{}", key))
        })?;
        let value = self
            .get_val(key)
            .ok_or_else(|| Error::with_msg("Invalid index").context("index", key.to_owned()))?;

        indexes.fold(Ok(value), |value, index| {
            let value = value?;
            let child = value.get(index);
            let child = child
                .ok_or_else(|| Error::with_msg("Invalid index").context("index", key.to_owned()))?;
            Ok(child)
        })
    }

    fn get_val<'a>(&'a self, name: &str) -> Option<&'a Value> {
        for frame in self.stack.iter().rev() {
            if let rval @ Some(_) = frame.get(name) {
                return rval;
            }
        }
        self.globals.and_then(|g| g.get(name))
    }

    /// Sets a value in the global context.
    pub fn set_global_val<S>(&mut self, name: S, val: Value) -> Option<Value>
    where
        S: Into<borrow::Cow<'static, str>>,
    {
        self.global_frame().insert(name.into(), val)
    }

    /// Sets a value to the rendering context.
    /// Note that it needs to be wrapped in a liquid::Value.
    ///
    /// # Panics
    ///
    /// Panics if there is no frame on the local values stack. Context
    /// instances are created with a top-level stack frame in place, so
    /// this should never happen in a well-formed program.
    pub fn set_val<S>(&mut self, name: S, val: Value) -> Option<Value>
    where
        S: Into<borrow::Cow<'static, str>>,
    {
        self.current_frame().insert(name.into(), val)
    }

    fn current_frame(&mut self) -> &mut Object {
        match self.stack.last_mut() {
            Some(frame) => frame,
            None => panic!("Global frame removed."),
        }
    }

    fn global_frame(&mut self) -> &mut Object {
        match self.stack.first_mut() {
            Some(frame) => frame,
            None => panic!("Global frame removed."),
        }
    }
}

impl<'g> Default for Stack<'g> {
    fn default() -> Self {
        Self::empty()
    }
}

/// Create processing context for a template.
pub struct ContextBuilder<'g> {
    globals: Option<&'g Globals>,
    filters: sync::Arc<HashMap<&'static str, BoxedValueFilter>>,
}

impl<'g> ContextBuilder<'g> {
    /// Creates a new, empty rendering context.
    pub fn new() -> Self {
        Self {
            globals: None,
            filters: Default::default(),
        }
    }

    /// Initialize the stack with the given globals.
    pub fn set_globals(mut self, values: &'g Globals) -> Self {
        self.globals = Some(values);
        self
    }

    /// Initialize the context with the given filters.
    pub fn set_filters(
        mut self,
        filters: &sync::Arc<HashMap<&'static str, BoxedValueFilter>>,
    ) -> Self {
        self.filters = sync::Arc::clone(filters);
        self
    }

    /// Create the `Context`.
    pub fn build(self) -> Context<'g> {
        let stack = match self.globals {
            Some(globals) => Stack::with_globals(globals),
            None => Stack::empty(),
        };
        Context {
            stack,
            interrupt: InterruptState::default(),
            cycles: CycleStateInner::default(),
            filters: self.filters,
        }
    }
}

impl<'g> Default for ContextBuilder<'g> {
    fn default() -> Self {
        Self::new()
    }
}

/// Processing context for a template.
#[derive(Default)]
pub struct Context<'g> {
    stack: Stack<'g>,

    interrupt: InterruptState,
    cycles: CycleStateInner,

    filters: sync::Arc<HashMap<&'static str, BoxedValueFilter>>,
}

impl<'g> Context<'g> {
    /// Create a default `Context`.
    ///
    /// See `ContextBuilder` for more control.
    pub fn new() -> Self {
        Context::default()
    }

    /// Grab a `FilterValue`.
    pub fn get_filter<'b>(&'b self, name: &str) -> Option<&'b FilterValue> {
        self.filters.get(name).map(|f| {
            let f: &FilterValue = f;
            f
        })
    }

    /// Access the block's `InterruptState`.
    pub fn interrupt(&self) -> &InterruptState {
        &self.interrupt
    }

    /// Access the block's `InterruptState`.
    pub fn interrupt_mut(&mut self) -> &mut InterruptState {
        &mut self.interrupt
    }

    /// See `cycle` tag.
    pub fn cycles<'a>(&'a mut self) -> CycleState<'a, 'g>
    where
        'g: 'a,
    {
        CycleState { context: self }
    }

    /// Access the current `Stack`.
    pub fn stack(&self) -> &Stack {
        &self.stack
    }

    /// Access the current `Stack`.
    pub fn stack_mut<'a>(&'a mut self) -> &'a mut Stack<'g>
    where
        'g: 'a,
    {
        &mut self.stack
    }

    /// Sets up a new stack frame, executes the supplied function and then
    /// tears the stack frame down before returning the function's result
    /// to the caller.
    pub fn run_in_scope<RvalT, FnT>(&mut self, f: FnT) -> RvalT
    where
        FnT: FnOnce(&mut Context) -> RvalT,
    {
        self.stack.push_frame();
        let result = f(self);
        self.stack.pop_frame();
        result
    }
}

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

    #[test]
    fn get_val() {
        let mut ctx = Context::new();
        ctx.stack_mut()
            .set_global_val("number", Value::scalar(42f64));
        assert_eq!(
            ctx.stack().get_val("number").unwrap(),
            &Value::scalar(42f64)
        );
    }

    #[test]
    fn get_val_failure() {
        let mut ctx = Context::new();
        let mut post = Object::new();
        post.insert("number".into(), Value::scalar(42f64));
        ctx.stack_mut().set_global_val("post", Value::Object(post));
        assert!(ctx.stack().get_val("post.number").is_none());
    }

    #[test]
    fn get_val_by_index() {
        let mut ctx = Context::new();
        let mut post = Object::new();
        post.insert("number".into(), Value::scalar(42f64));
        ctx.stack_mut().set_global_val("post", Value::Object(post));
        let indexes = vec![Index::with_key("post"), Index::with_key("number")];
        assert_eq!(
            ctx.stack().get_val_by_index(indexes.iter()).unwrap(),
            &Value::scalar(42f64)
        );
    }

    #[test]
    fn scoped_variables() {
        let mut ctx = Context::new();
        ctx.stack_mut().set_global_val("test", Value::scalar(42f64));
        assert_eq!(ctx.stack().get_val("test").unwrap(), &Value::scalar(42f64));

        ctx.run_in_scope(|new_scope| {
            // assert that values are chained to the parent scope
            assert_eq!(
                new_scope.stack().get_val("test").unwrap(),
                &Value::scalar(42f64)
            );

            // set a new local value, and assert that it overrides the previous value
            new_scope
                .stack_mut()
                .set_val("test", Value::scalar(3.14f64));
            assert_eq!(
                new_scope.stack().get_val("test").unwrap(),
                &Value::scalar(3.14f64)
            );

            // sat a new val that we will pick up outside the scope
            new_scope
                .stack_mut()
                .set_global_val("global", Value::scalar("some value"));
        });

        // assert that the value has reverted to the old one
        assert_eq!(ctx.stack().get_val("test").unwrap(), &Value::scalar(42f64));
        assert_eq!(
            ctx.stack().get_val("global").unwrap(),
            &Value::scalar("some value")
        );
    }
}
<|endoftext|>