<commit_before>use std::collections::{hash_map::Entry, HashMap, VecDeque};
use std::convert::TryFrom;
use std::num::NonZeroU32;

use rustc_index::vec::{Idx, IndexVec};

use crate::*;

macro_rules! declare_id {
    ($name: ident) => {
        /// 0 is used to indicate that the id was not yet assigned and,
        /// therefore, is not a valid identifier.
        #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
        pub struct $name(NonZeroU32);

        impl $name {
            // Panics if `id == 0`.
            pub fn from_u32(id: u32) -> Self {
                Self(NonZeroU32::new(id).unwrap())
            }
        }

        impl Idx for $name {
            fn new(idx: usize) -> Self {
                $name(NonZeroU32::new(u32::try_from(idx).unwrap() + 1).unwrap())
            }
            fn index(self) -> usize {
                usize::try_from(self.0.get() - 1).unwrap()
            }
        }

        impl $name {
            pub fn to_u32_scalar<'tcx>(&self) -> Scalar<Tag> {
                Scalar::from_u32(self.0.get())
            }
        }
    };
}

declare_id!(MutexId);

/// The mutex state.
#[derive(Default, Debug)]
struct Mutex {
    /// The thread that currently owns the lock.
    owner: Option<ThreadId>,
    /// How many times the mutex was locked by the owner.
    lock_count: usize,
    /// The queue of threads waiting for this mutex.
    queue: VecDeque<ThreadId>,
}

declare_id!(RwLockId);

/// The read-write lock state.
#[derive(Default, Debug)]
struct RwLock {
    /// The writer thread that currently owns the lock.
    writer: Option<ThreadId>,
    /// The readers that currently own the lock and how many times they acquired
    /// the lock.
    readers: HashMap<ThreadId, usize>,
    /// The queue of writer threads waiting for this lock.
    writer_queue: VecDeque<ThreadId>,
    /// The queue of reader threads waiting for this lock.
    reader_queue: VecDeque<ThreadId>,
}

declare_id!(CondvarId);

/// A thread waiting on a conditional variable.
#[derive(Debug)]
struct CondvarWaiter {
    /// The thread that is waiting on this variable.
    thread: ThreadId,
    /// The mutex on which the thread is waiting.
    mutex: MutexId,
}

/// The conditional variable state.
#[derive(Default, Debug)]
struct Condvar {
    waiters: VecDeque<CondvarWaiter>,
}

/// The state of all synchronization variables.
#[derive(Default, Debug)]
pub(super) struct SynchronizationState {
    mutexes: IndexVec<MutexId, Mutex>,
    rwlocks: IndexVec<RwLockId, RwLock>,
    condvars: IndexVec<CondvarId, Condvar>,
}

// Public interface to synchronization primitives. Please note that in most
// cases, the function calls are infallible and it is the client's (shim
// implementation's) responsibility to detect and deal with erroneous
// situations.
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    #[inline]
    /// Create state for a new mutex.
    fn mutex_create(&mut self) -> MutexId {
        let this = self.eval_context_mut();
        this.machine.threads.sync.mutexes.push(Default::default())
    }

    #[inline]
    /// Get the id of the thread that currently owns this lock.
    fn mutex_get_owner(&mut self, id: MutexId) -> ThreadId {
        let this = self.eval_context_ref();
        this.machine.threads.sync.mutexes[id].owner.unwrap()
    }

    #[inline]
    /// Check if locked.
    fn mutex_is_locked(&mut self, id: MutexId) -> bool {
        let this = self.eval_context_mut();
        this.machine.threads.sync.mutexes[id].owner.is_some()
    }

    /// Lock by setting the mutex owner and increasing the lock count.
    fn mutex_lock(&mut self, id: MutexId, thread: ThreadId) {
        let this = self.eval_context_mut();
        let mutex = &mut this.machine.threads.sync.mutexes[id];
        if let Some(current_owner) = mutex.owner {
            assert_eq!(thread, current_owner, "mutex already locked by another thread");
            assert!(
                mutex.lock_count > 0,
                "invariant violation: lock_count == 0 iff the thread is unlocked"
            );
        } else {
            mutex.owner = Some(thread);
        }
        mutex.lock_count = mutex.lock_count.checked_add(1).unwrap();
    }

    /// Unlock by decreasing the lock count. If the lock count reaches 0, unset
    /// the owner.
    fn mutex_unlock(&mut self, id: MutexId) -> Option<(ThreadId, usize)> {
        let this = self.eval_context_mut();
        let mutex = &mut this.machine.threads.sync.mutexes[id];
        if let Some(current_owner) = mutex.owner {
            mutex.lock_count = mutex
                .lock_count
                .checked_sub(1)
                .expect("invariant violation: lock_count == 0 iff the thread is unlocked");
            if mutex.lock_count == 0 {
                mutex.owner = None;
            }
            Some((current_owner, mutex.lock_count))
        } else {
            None
        }
    }

    #[inline]
    /// Take a thread out the queue waiting for the lock.
    fn mutex_enqueue(&mut self, id: MutexId, thread: ThreadId) {
        let this = self.eval_context_mut();
        this.machine.threads.sync.mutexes[id].queue.push_back(thread);
    }

    #[inline]
    /// Take a thread out the queue waiting for the lock.
    fn mutex_dequeue(&mut self, id: MutexId) -> Option<ThreadId> {
        let this = self.eval_context_mut();
        this.machine.threads.sync.mutexes[id].queue.pop_front()
    }

    #[inline]
    /// Create state for a new read write lock.
    fn rwlock_create(&mut self) -> RwLockId {
        let this = self.eval_context_mut();
        this.machine.threads.sync.rwlocks.push(Default::default())
    }

    #[inline]
    /// Check if locked.
    fn rwlock_is_locked(&mut self, id: RwLockId) -> bool {
        let this = self.eval_context_mut();
        this.machine.threads.sync.rwlocks[id].writer.is_some()
            || !this.machine.threads.sync.rwlocks[id].readers.is_empty()
    }

    #[inline]
    /// Check if write locked.
    fn rwlock_is_write_locked(&mut self, id: RwLockId) -> bool {
        let this = self.eval_context_mut();
        this.machine.threads.sync.rwlocks[id].writer.is_some()
    }

    /// Add a reader that collectively with other readers owns the lock.
    fn rwlock_reader_add(&mut self, id: RwLockId, reader: ThreadId) {
        let this = self.eval_context_mut();
        assert!(!this.rwlock_is_write_locked(id), "the lock is write locked");
        let count = this.machine.threads.sync.rwlocks[id].readers.entry(reader).or_insert(0);
        *count += 1;
    }

    /// Try removing the reader. Returns `true` if succeeded.
    fn rwlock_reader_remove(&mut self, id: RwLockId, reader: ThreadId) -> bool {
        let this = self.eval_context_mut();
        match this.machine.threads.sync.rwlocks[id].readers.entry(reader) {
            Entry::Occupied(mut entry) => {
                let count = entry.get_mut();
                *count -= 1;
                if *count == 0 {
                    entry.remove();
                }
                true
            }
            Entry::Vacant(_) => false,
        }
    }

    #[inline]
    /// Put the reader in the queue waiting for the lock.
    fn rwlock_enqueue_reader(&mut self, id: RwLockId, reader: ThreadId) {
        let this = self.eval_context_mut();
        assert!(this.rwlock_is_write_locked(id), "queueing on not write locked lock");
        this.machine.threads.sync.rwlocks[id].reader_queue.push_back(reader);
    }

    #[inline]
    /// Take the reader out the queue waiting for the lock.
    fn rwlock_dequeue_reader(&mut self, id: RwLockId) -> Option<ThreadId> {
        let this = self.eval_context_mut();
        this.machine.threads.sync.rwlocks[id].reader_queue.pop_front()
    }

    #[inline]
    /// Lock by setting the writer that owns the lock.
    fn rwlock_writer_set(&mut self, id: RwLockId, writer: ThreadId) {
        let this = self.eval_context_mut();
        assert!(!this.rwlock_is_locked(id), "the lock is already locked");
        this.machine.threads.sync.rwlocks[id].writer = Some(writer);
    }

    #[inline]
    /// Try removing the writer.
    fn rwlock_writer_remove(&mut self, id: RwLockId) -> Option<ThreadId> {
        let this = self.eval_context_mut();
        this.machine.threads.sync.rwlocks[id].writer.take()
    }

    #[inline]
    /// Put the writer in the queue waiting for the lock.
    fn rwlock_enqueue_writer(&mut self, id: RwLockId, writer: ThreadId) {
        let this = self.eval_context_mut();
        assert!(this.rwlock_is_locked(id), "queueing on unlocked lock");
        this.machine.threads.sync.rwlocks[id].writer_queue.push_back(writer);
    }

    #[inline]
    /// Take the writer out the queue waiting for the lock.
    fn rwlock_dequeue_writer(&mut self, id: RwLockId) -> Option<ThreadId> {
        let this = self.eval_context_mut();
        this.machine.threads.sync.rwlocks[id].writer_queue.pop_front()
    }

    #[inline]
    /// Create state for a new conditional variable.
    fn condvar_create(&mut self) -> CondvarId {
        let this = self.eval_context_mut();
        this.machine.threads.sync.condvars.push(Default::default())
    }

    #[inline]
    /// Is the conditional variable awaited?
    fn condvar_is_awaited(&mut self, id: CondvarId) -> bool {
        let this = self.eval_context_mut();
        !this.machine.threads.sync.condvars[id].waiters.is_empty()
    }

    /// Mark that the thread is waiting on the conditional variable.
    fn condvar_wait(&mut self, id: CondvarId, thread: ThreadId, mutex: MutexId) {
        let this = self.eval_context_mut();
        let waiters = &mut this.machine.threads.sync.condvars[id].waiters;
        assert!(waiters.iter().all(|waiter| waiter.thread != thread), "thread is already waiting");
        waiters.push_back(CondvarWaiter { thread, mutex });
    }

    /// Wake up some thread (if there is any) sleeping on the conditional
    /// variable.
    fn condvar_signal(&mut self, id: CondvarId) -> Option<(ThreadId, MutexId)> {
        let this = self.eval_context_mut();
        this.machine.threads.sync.condvars[id]
            .waiters
            .pop_front()
            .map(|waiter| (waiter.thread, waiter.mutex))
    }

    #[inline]
    /// Remove the thread from the queue of threads waiting on this conditional variable.
    fn condvar_remove_waiter(&mut self, id: CondvarId, thread: ThreadId) {
        let this = self.eval_context_mut();
        this.machine.threads.sync.condvars[id].waiters.retain(|waiter| waiter.thread != thread);
    }
}
<commit_msg>Add comments explaining the declare_id macro.<commit_after>use std::collections::{hash_map::Entry, HashMap, VecDeque};
use std::convert::TryFrom;
use std::num::NonZeroU32;

use rustc_index::vec::{Idx, IndexVec};

use crate::*;

/// We cannot use the `newtype_index!` macro because we have to use 0 as a
/// sentinel value meaning that the identifier is not assigned. This is because
/// the pthreads static initializers initialize memory with zeros (see the
/// `src/shims/sync.rs` file).
macro_rules! declare_id {
    ($name: ident) => {
        /// 0 is used to indicate that the id was not yet assigned and,
        /// therefore, is not a valid identifier.
        #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
        pub struct $name(NonZeroU32);

        impl $name {
            // Panics if `id == 0`.
            pub fn from_u32(id: u32) -> Self {
                Self(NonZeroU32::new(id).unwrap())
            }
        }

        impl Idx for $name {
            fn new(idx: usize) -> Self {
                // We use 0 as a sentinel value (see the comment above) and,
                // therefore, need to shift by one when converting from an index
                // into a vector.
                $name(NonZeroU32::new(u32::try_from(idx).unwrap() + 1).unwrap())
            }
            fn index(self) -> usize {
                // See the comment in `Self::new`.
                usize::try_from(self.0.get() - 1).unwrap()
            }
        }

        impl $name {
            pub fn to_u32_scalar<'tcx>(&self) -> Scalar<Tag> {
                Scalar::from_u32(self.0.get())
            }
        }
    };
}

declare_id!(MutexId);

/// The mutex state.
#[derive(Default, Debug)]
struct Mutex {
    /// The thread that currently owns the lock.
    owner: Option<ThreadId>,
    /// How many times the mutex was locked by the owner.
    lock_count: usize,
    /// The queue of threads waiting for this mutex.
    queue: VecDeque<ThreadId>,
}

declare_id!(RwLockId);

/// The read-write lock state.
#[derive(Default, Debug)]
struct RwLock {
    /// The writer thread that currently owns the lock.
    writer: Option<ThreadId>,
    /// The readers that currently own the lock and how many times they acquired
    /// the lock.
    readers: HashMap<ThreadId, usize>,
    /// The queue of writer threads waiting for this lock.
    writer_queue: VecDeque<ThreadId>,
    /// The queue of reader threads waiting for this lock.
    reader_queue: VecDeque<ThreadId>,
}

declare_id!(CondvarId);

/// A thread waiting on a conditional variable.
#[derive(Debug)]
struct CondvarWaiter {
    /// The thread that is waiting on this variable.
    thread: ThreadId,
    /// The mutex on which the thread is waiting.
    mutex: MutexId,
}

/// The conditional variable state.
#[derive(Default, Debug)]
struct Condvar {
    waiters: VecDeque<CondvarWaiter>,
}

/// The state of all synchronization variables.
#[derive(Default, Debug)]
pub(super) struct SynchronizationState {
    mutexes: IndexVec<MutexId, Mutex>,
    rwlocks: IndexVec<RwLockId, RwLock>,
    condvars: IndexVec<CondvarId, Condvar>,
}

// Public interface to synchronization primitives. Please note that in most
// cases, the function calls are infallible and it is the client's (shim
// implementation's) responsibility to detect and deal with erroneous
// situations.
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    #[inline]
    /// Create state for a new mutex.
    fn mutex_create(&mut self) -> MutexId {
        let this = self.eval_context_mut();
        this.machine.threads.sync.mutexes.push(Default::default())
    }

    #[inline]
    /// Get the id of the thread that currently owns this lock.
    fn mutex_get_owner(&mut self, id: MutexId) -> ThreadId {
        let this = self.eval_context_ref();
        this.machine.threads.sync.mutexes[id].owner.unwrap()
    }

    #[inline]
    /// Check if locked.
    fn mutex_is_locked(&mut self, id: MutexId) -> bool {
        let this = self.eval_context_mut();
        this.machine.threads.sync.mutexes[id].owner.is_some()
    }

    /// Lock by setting the mutex owner and increasing the lock count.
    fn mutex_lock(&mut self, id: MutexId, thread: ThreadId) {
        let this = self.eval_context_mut();
        let mutex = &mut this.machine.threads.sync.mutexes[id];
        if let Some(current_owner) = mutex.owner {
            assert_eq!(thread, current_owner, "mutex already locked by another thread");
            assert!(
                mutex.lock_count > 0,
                "invariant violation: lock_count == 0 iff the thread is unlocked"
            );
        } else {
            mutex.owner = Some(thread);
        }
        mutex.lock_count = mutex.lock_count.checked_add(1).unwrap();
    }

    /// Unlock by decreasing the lock count. If the lock count reaches 0, unset
    /// the owner.
    fn mutex_unlock(&mut self, id: MutexId) -> Option<(ThreadId, usize)> {
        let this = self.eval_context_mut();
        let mutex = &mut this.machine.threads.sync.mutexes[id];
        if let Some(current_owner) = mutex.owner {
            mutex.lock_count = mutex
                .lock_count
                .checked_sub(1)
                .expect("invariant violation: lock_count == 0 iff the thread is unlocked");
            if mutex.lock_count == 0 {
                mutex.owner = None;
            }
            Some((current_owner, mutex.lock_count))
        } else {
            None
        }
    }

    #[inline]
    /// Take a thread out the queue waiting for the lock.
    fn mutex_enqueue(&mut self, id: MutexId, thread: ThreadId) {
        let this = self.eval_context_mut();
        this.machine.threads.sync.mutexes[id].queue.push_back(thread);
    }

    #[inline]
    /// Take a thread out the queue waiting for the lock.
    fn mutex_dequeue(&mut self, id: MutexId) -> Option<ThreadId> {
        let this = self.eval_context_mut();
        this.machine.threads.sync.mutexes[id].queue.pop_front()
    }

    #[inline]
    /// Create state for a new read write lock.
    fn rwlock_create(&mut self) -> RwLockId {
        let this = self.eval_context_mut();
        this.machine.threads.sync.rwlocks.push(Default::default())
    }

    #[inline]
    /// Check if locked.
    fn rwlock_is_locked(&mut self, id: RwLockId) -> bool {
        let this = self.eval_context_mut();
        this.machine.threads.sync.rwlocks[id].writer.is_some()
            || !this.machine.threads.sync.rwlocks[id].readers.is_empty()
    }

    #[inline]
    /// Check if write locked.
    fn rwlock_is_write_locked(&mut self, id: RwLockId) -> bool {
        let this = self.eval_context_mut();
        this.machine.threads.sync.rwlocks[id].writer.is_some()
    }

    /// Add a reader that collectively with other readers owns the lock.
    fn rwlock_reader_add(&mut self, id: RwLockId, reader: ThreadId) {
        let this = self.eval_context_mut();
        assert!(!this.rwlock_is_write_locked(id), "the lock is write locked");
        let count = this.machine.threads.sync.rwlocks[id].readers.entry(reader).or_insert(0);
        *count += 1;
    }

    /// Try removing the reader. Returns `true` if succeeded.
    fn rwlock_reader_remove(&mut self, id: RwLockId, reader: ThreadId) -> bool {
        let this = self.eval_context_mut();
        match this.machine.threads.sync.rwlocks[id].readers.entry(reader) {
            Entry::Occupied(mut entry) => {
                let count = entry.get_mut();
                *count -= 1;
                if *count == 0 {
                    entry.remove();
                }
                true
            }
            Entry::Vacant(_) => false,
        }
    }

    #[inline]
    /// Put the reader in the queue waiting for the lock.
    fn rwlock_enqueue_reader(&mut self, id: RwLockId, reader: ThreadId) {
        let this = self.eval_context_mut();
        assert!(this.rwlock_is_write_locked(id), "queueing on not write locked lock");
        this.machine.threads.sync.rwlocks[id].reader_queue.push_back(reader);
    }

    #[inline]
    /// Take the reader out the queue waiting for the lock.
    fn rwlock_dequeue_reader(&mut self, id: RwLockId) -> Option<ThreadId> {
        let this = self.eval_context_mut();
        this.machine.threads.sync.rwlocks[id].reader_queue.pop_front()
    }

    #[inline]
    /// Lock by setting the writer that owns the lock.
    fn rwlock_writer_set(&mut self, id: RwLockId, writer: ThreadId) {
        let this = self.eval_context_mut();
        assert!(!this.rwlock_is_locked(id), "the lock is already locked");
        this.machine.threads.sync.rwlocks[id].writer = Some(writer);
    }

    #[inline]
    /// Try removing the writer.
    fn rwlock_writer_remove(&mut self, id: RwLockId) -> Option<ThreadId> {
        let this = self.eval_context_mut();
        this.machine.threads.sync.rwlocks[id].writer.take()
    }

    #[inline]
    /// Put the writer in the queue waiting for the lock.
    fn rwlock_enqueue_writer(&mut self, id: RwLockId, writer: ThreadId) {
        let this = self.eval_context_mut();
        assert!(this.rwlock_is_locked(id), "queueing on unlocked lock");
        this.machine.threads.sync.rwlocks[id].writer_queue.push_back(writer);
    }

    #[inline]
    /// Take the writer out the queue waiting for the lock.
    fn rwlock_dequeue_writer(&mut self, id: RwLockId) -> Option<ThreadId> {
        let this = self.eval_context_mut();
        this.machine.threads.sync.rwlocks[id].writer_queue.pop_front()
    }

    #[inline]
    /// Create state for a new conditional variable.
    fn condvar_create(&mut self) -> CondvarId {
        let this = self.eval_context_mut();
        this.machine.threads.sync.condvars.push(Default::default())
    }

    #[inline]
    /// Is the conditional variable awaited?
    fn condvar_is_awaited(&mut self, id: CondvarId) -> bool {
        let this = self.eval_context_mut();
        !this.machine.threads.sync.condvars[id].waiters.is_empty()
    }

    /// Mark that the thread is waiting on the conditional variable.
    fn condvar_wait(&mut self, id: CondvarId, thread: ThreadId, mutex: MutexId) {
        let this = self.eval_context_mut();
        let waiters = &mut this.machine.threads.sync.condvars[id].waiters;
        assert!(waiters.iter().all(|waiter| waiter.thread != thread), "thread is already waiting");
        waiters.push_back(CondvarWaiter { thread, mutex });
    }

    /// Wake up some thread (if there is any) sleeping on the conditional
    /// variable.
    fn condvar_signal(&mut self, id: CondvarId) -> Option<(ThreadId, MutexId)> {
        let this = self.eval_context_mut();
        this.machine.threads.sync.condvars[id]
            .waiters
            .pop_front()
            .map(|waiter| (waiter.thread, waiter.mutex))
    }

    #[inline]
    /// Remove the thread from the queue of threads waiting on this conditional variable.
    fn condvar_remove_waiter(&mut self, id: CondvarId, thread: ThreadId) {
        let this = self.eval_context_mut();
        this.machine.threads.sync.condvars[id].waiters.retain(|waiter| waiter.thread != thread);
    }
}
<|endoftext|>


<commit_before>use std::fs::{File, OpenOptions, PathExt};
use std::io::{Read, Write};
use std::process::Command;
use std::collections::HashSet;
use std::path::{PathBuf, AsPath};

use app_result::{AppResult, app_err};
use types::{Tags, TagsKind, SourceKind};

use dirs::{
   rusty_tags_cache_dir,
   cargo_git_src_dir,
   cargo_crates_io_src_dir,
   glob_path
};

/// Checks if there's already a tags file for `source`
/// and if not it's creating a new tags file and returning it.
pub fn update_tags(tags_kind: &TagsKind, source: &SourceKind) -> AppResult<Tags>
{
   let cache_dir = try!(rusty_tags_cache_dir());

   let mut src_tags = cache_dir.clone();
   src_tags.push(&source.tags_file_name(tags_kind));

   let src_dir = try!(find_src_dir(source));
   if src_tags.as_path().is_file() {
      return Ok(Tags::new(&src_dir, &src_tags, true));
   }

   try!(create_tags(tags_kind, &src_dir, &src_tags));
   Ok(Tags::new(&src_dir, &src_tags, false))
}

/// Does the same thing as `update_tags`, but also checks if the `lib.rs`
/// file of the library has public reexports of external crates. If
/// that's the case, then the tags of the public reexported external
/// crates are merged into the tags of the library.
pub fn update_tags_and_check_for_reexports(tags_kind: &TagsKind,
                                           source: &SourceKind,
                                           dependencies: &Vec<SourceKind>) -> AppResult<Tags>
{
   let lib_tags = try!(update_tags(tags_kind, source));
   if lib_tags.is_up_to_date(tags_kind) {
      return Ok(lib_tags);
   }

   let reexp_crates = try!(find_reexported_crates(&lib_tags.src_dir));
   if reexp_crates.is_empty() {
      return Ok(lib_tags);
   }

   println!("Found public reexports in '{}' of:", source.get_lib_name());
   for rcrate in reexp_crates.iter() {
      println!("   {}", rcrate);
   }
   println!("");

   let mut crate_tags = Vec::<PathBuf>::new();
   for rcrate in reexp_crates.iter() {
      if let Some(crate_dep) = dependencies.iter().find(|d| d.get_lib_name() == *rcrate) {
         crate_tags.push(try!(update_tags(tags_kind, crate_dep)).tags_file.clone());
      }
   }

   if crate_tags.is_empty() {
      return Ok(lib_tags);
   }

   crate_tags.push(lib_tags.tags_file.clone());
   try!(merge_tags(tags_kind, &crate_tags, &lib_tags.tags_file));
   Ok(lib_tags)
}

/// merges `tag_files` into `into_tag_file`
pub fn merge_tags<P: AsPath>(tags_kind: &TagsKind, tag_files: &Vec<PathBuf>, into_tag_file: &P) -> AppResult<()>
{
   println!("Merging ...\n   tags:");

   for file in tag_files.iter() {
      println!("      {}", file.display());
   }

   println!("\n   into:\n      {}\n", into_tag_file.as_path().display());

   match *tags_kind {
      TagsKind::Vi => {
         let mut file_contents: Vec<String> = Vec::new();
         for file in tag_files.iter() {
            let mut file = try!(File::open(file));
            let mut contents = String::new();
            try!(file.read_to_string(&mut contents));
            file_contents.push(contents);
         }

         let mut merged_lines: Vec<&str> = Vec::with_capacity(100_000);
         for content in file_contents.iter() {
            for line in content.as_slice().lines_any() {
               if ! line.is_empty() && line.char_at(0) != '!' {
                  merged_lines.push(line);
               }
            }
         }

         merged_lines.sort();
         merged_lines.dedup();

         let mut tag_file = try!(
             OpenOptions::new()
                .truncate(true)
                .read(true)
                .write(true)
                .open(into_tag_file)
         );

         try!(tag_file.write_fmt(format_args!("{}\n", "!_TAG_FILE_FORMAT	2	/extended format; --format=1 will not append ;\" to lines/")));
         try!(tag_file.write_fmt(format_args!("{}\n", "!_TAG_FILE_SORTED	1	/0=unsorted, 1=sorted, 2=foldcase/")));

         for line in merged_lines.iter() {
            try!(tag_file.write_fmt(format_args!("{}\n", *line)));
         }
      },

      TagsKind::Emacs => {
         let mut tag_file = try!(
             OpenOptions::new()
                .append(true)
                .read(true)
                .write(true)
                .open(into_tag_file)
         );

         for file in tag_files.iter() {
            if file.as_path() != into_tag_file.as_path() {
               try!(tag_file.write_fmt(format_args!("{},include\n", file.display())));
            }
         }
      }
   }

   Ok(())
}

/// creates tags recursive for the directory hierarchy starting at `src_dir`
/// and writes them to `tags_file`
pub fn create_tags<P: AsPath>(tags_kind: &TagsKind, src_dir: &P, tags_file: &P) -> AppResult<()>
{
   let mut cmd = Command::new("ctags");

   tags_kind.ctags_option().map(|opt| { cmd.arg(opt); () });

   cmd.arg("--recurse")
      .arg("--languages=Rust")
      .arg("--langdef=Rust")
      .arg("--langmap=Rust:.rs")
      .arg("--regex-Rust=/^[ \\t]*(#\\[[^\\]]\\][ \\t]*)*(pub[ \\t]+)?(extern[ \\t]+)?(\"[^\"]+\"[ \\t]+)?(unsafe[ \\t]+)?fn[ \\t]+([a-zA-Z0-9_]+)/\\6/f,functions,function definitions/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?type[ \\t]+([a-zA-Z0-9_]+)/\\2/T,types,type definitions/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?enum[ \\t]+([a-zA-Z0-9_]+)/\\2/g,enum,enumeration names/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?struct[ \\t]+([a-zA-Z0-9_]+)/\\2/s,structure names/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?mod[ \\t]+([a-zA-Z0-9_]+)/\\2/m,modules,module names/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?static[ \\t]+([a-zA-Z0-9_]+)/\\2/c,consts,static constants/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?trait[ \\t]+([a-zA-Z0-9_]+)/\\2/t,traits,traits/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?impl([ \\t\\n]*<[^>]*>)?[ \\t]+(([a-zA-Z0-9_:]+)[ \\t]*(<[^>]*>)?[ \\t]+(for)[ \\t]+)?([a-zA-Z0-9_]+)/\\4 \\6 \\7/i,impls,trait implementations/")
      .arg("--regex-Rust=/^[ \\t]*macro_rules![ \\t]+([a-zA-Z0-9_]+)/\\1/d,macros,macro definitions/")
      .arg("-o")
      .arg(tags_file.as_path())
      .arg(src_dir.as_path());

   println!("Creating tags ...\n   for source:\n      {}\n\n   cached at:\n      {}\n",
            src_dir.as_path().display(), tags_file.as_path().display());

   try!(cmd.output());
   Ok(())
}

/// find the source directory of `source`, for git sources the directories
/// in `~/.cargo/git/checkouts` are considered and for crates.io sources
/// the directories in `~/.cargo/registry/src/github.com-*` are considered
fn find_src_dir(source: &SourceKind) -> AppResult<PathBuf>
{
   match *source {
      SourceKind::Git { ref lib_name, ref commit_hash } => {
         let mut lib_src = lib_name.clone();
         lib_src.push_str("-*");

         let mut src_dir = try!(cargo_git_src_dir());
         src_dir.push(&lib_src);
         src_dir.push("master");

         let src_paths = try!(glob_path(&format!("{}", src_dir.display())));
         for src_path in src_paths {
            if let Ok(path) = src_path {
               let src_path_buf = PathBuf::new(&format!("{}", path.display()));
               let src_commit_hash = try!(get_commit_hash(&src_path_buf));
               if *commit_hash == src_commit_hash {
                  return Ok(src_path_buf);
               }
            }
         }

         // the git repository name hasn't to match the name of the library,
         // so here we're just going through all git directories and searching
         // for the one with a matching commit hash
         let mut src_dir = try!(cargo_git_src_dir());
         src_dir.push("*");
         src_dir.push("master");

         let src_paths = try!(glob_path(&format!("{}", src_dir.display())));
         for src_path in src_paths {
            if let Ok(path) = src_path {
               let src_path_buf = PathBuf::new(&format!("{}", path.display()));
               let src_commit_hash = try!(get_commit_hash(&src_path_buf));
               if *commit_hash == src_commit_hash {
                  return Ok(src_path_buf);
               }
            }
         }

         Err(app_err(format!("
   Couldn't find git repository of the dependency '{}'!
   Have you run 'cargo build' at least once or have you added/updated a dependency without calling 'cargo build' again?", lib_name)))
      },

      SourceKind::CratesIo { ref lib_name, ref version } => {
         let mut lib_src = lib_name.clone();
         lib_src.push('-');
         lib_src.push_str(&**version);

         let mut src_dir = try!(cargo_crates_io_src_dir());
         src_dir.push(&lib_src);

         if ! src_dir.is_dir() {
            return Err(app_err(format!("
   Couldn't find source code of the dependency '{}'!
   Have you run 'cargo build' at least once or have you added/updated a dependency without calling 'cargo build' again?", lib_name)))
         }

         Ok(src_dir)
      }
   }
}

type CrateName = String;

/// searches in the file `<src_dir>/src/lib.rs` for external crates
/// that are reexpored and returns their names
fn find_reexported_crates<P: AsPath>(src_dir: &P) -> AppResult<Vec<CrateName>>
{
   let mut lib_file = src_dir.as_path().to_path_buf();
   lib_file.push("src");
   lib_file.push("lib.rs");

   if ! lib_file.is_file() {
      return Ok(Vec::new());
   }

   let contents = {
      let mut file = try!(File::open(&lib_file));
      let mut contents = String::new();
      try!(file.read_to_string(&mut contents));
      contents
   };

   let lines = contents.lines_any();

   type ModuleName = String;
   let mut pub_uses = HashSet::<ModuleName>::new();

   #[derive(Eq, PartialEq, Hash)]
   struct ExternCrate<'a>
   {
      name: &'a str,
      as_name: &'a str
   }

   let mut extern_crates = HashSet::<ExternCrate>::new();

   for line in lines {
      let items = line.trim_matches(';').split(' ').collect::<Vec<&str>>();
      if items.len() < 3 {
         continue;
      }

      if items[0] == "pub" && items[1] == "use" {
         let mods = items[2].split("::").collect::<Vec<&str>>();
         if mods.len() >= 1 {
            pub_uses.insert(mods[0].to_string());
         }
      }

      if items[0] == "extern" && items[1] == "crate" {
         if items.len() == 3 {
            extern_crates.insert(ExternCrate { name: items[2].trim_matches('"'), as_name: items[2] });
         }
         else if items.len() == 5 && items[3] == "as" {
            extern_crates.insert(ExternCrate { name: items[2].trim_matches('"'), as_name: items[4] });
         }
      }
   }

   let mut reexp_crates = Vec::<CrateName>::new();
   for extern_crate in extern_crates.iter() {
      if pub_uses.contains(extern_crate.as_name) {
         reexp_crates.push(extern_crate.name.to_string());
      }
   }

   Ok(reexp_crates)
}

/// get the commit hash of the current `HEAD` of the git repository located at `git_dir`
fn get_commit_hash<P: AsPath>(git_dir: &P) -> AppResult<String>
{
   let mut cmd = Command::new("git");
   cmd.current_dir(git_dir)
      .arg("rev-parse")
      .arg("HEAD");

   let out = try!(cmd.output());
   String::from_utf8(out.stdout)
      .map(|s| s.as_slice().trim().to_string())
      .map_err(|_| app_err("Couldn't convert git output to utf8!".to_string()))
}
<commit_msg>Fixes for file handling in Rust 1.0<commit_after>use std::fs::{File, OpenOptions, PathExt};
use std::io::{Read, Write};
use std::process::Command;
use std::collections::HashSet;
use std::path::{PathBuf, AsPath};

use app_result::{AppResult, app_err};
use types::{Tags, TagsKind, SourceKind};

use dirs::{
   rusty_tags_cache_dir,
   cargo_git_src_dir,
   cargo_crates_io_src_dir,
   glob_path
};

/// Checks if there's already a tags file for `source`
/// and if not it's creating a new tags file and returning it.
pub fn update_tags(tags_kind: &TagsKind, source: &SourceKind) -> AppResult<Tags>
{
   let cache_dir = try!(rusty_tags_cache_dir());

   let mut src_tags = cache_dir.clone();
   src_tags.push(&source.tags_file_name(tags_kind));

   let src_dir = try!(find_src_dir(source));
   if src_tags.as_path().is_file() {
      return Ok(Tags::new(&src_dir, &src_tags, true));
   }

   try!(create_tags(tags_kind, &src_dir, &src_tags));
   Ok(Tags::new(&src_dir, &src_tags, false))
}

/// Does the same thing as `update_tags`, but also checks if the `lib.rs`
/// file of the library has public reexports of external crates. If
/// that's the case, then the tags of the public reexported external
/// crates are merged into the tags of the library.
pub fn update_tags_and_check_for_reexports(tags_kind: &TagsKind,
                                           source: &SourceKind,
                                           dependencies: &Vec<SourceKind>) -> AppResult<Tags>
{
   let lib_tags = try!(update_tags(tags_kind, source));
   if lib_tags.is_up_to_date(tags_kind) {
      return Ok(lib_tags);
   }

   let reexp_crates = try!(find_reexported_crates(&lib_tags.src_dir));
   if reexp_crates.is_empty() {
      return Ok(lib_tags);
   }

   println!("Found public reexports in '{}' of:", source.get_lib_name());
   for rcrate in reexp_crates.iter() {
      println!("   {}", rcrate);
   }
   println!("");

   let mut crate_tags = Vec::<PathBuf>::new();
   for rcrate in reexp_crates.iter() {
      if let Some(crate_dep) = dependencies.iter().find(|d| d.get_lib_name() == *rcrate) {
         crate_tags.push(try!(update_tags(tags_kind, crate_dep)).tags_file.clone());
      }
   }

   if crate_tags.is_empty() {
      return Ok(lib_tags);
   }

   crate_tags.push(lib_tags.tags_file.clone());
   try!(merge_tags(tags_kind, &crate_tags, &lib_tags.tags_file));
   Ok(lib_tags)
}

/// merges `tag_files` into `into_tag_file`
pub fn merge_tags<P: AsPath>(tags_kind: &TagsKind, tag_files: &Vec<PathBuf>, into_tag_file: &P) -> AppResult<()>
{
   println!("Merging ...\n   tags:");

   for file in tag_files.iter() {
      println!("      {}", file.display());
   }

   println!("\n   into:\n      {}\n", into_tag_file.as_path().display());

   match *tags_kind {
      TagsKind::Vi => {
         let mut file_contents: Vec<String> = Vec::new();
         for file in tag_files.iter() {
            let mut file = try!(File::open(file));
            let mut contents = String::new();
            try!(file.read_to_string(&mut contents));
            file_contents.push(contents);
         }

         let mut merged_lines: Vec<&str> = Vec::with_capacity(100_000);
         for content in file_contents.iter() {
            for line in content.as_slice().lines_any() {
               if ! line.is_empty() && line.char_at(0) != '!' {
                  merged_lines.push(line);
               }
            }
         }

         merged_lines.sort();
         merged_lines.dedup();

         let mut tag_file = try!(
             OpenOptions::new()
                .create(true)
                .truncate(true)
                .read(true)
                .write(true)
                .open(into_tag_file)
         );

         try!(tag_file.write_fmt(format_args!("{}\n", "!_TAG_FILE_FORMAT	2	/extended format; --format=1 will not append ;\" to lines/")));
         try!(tag_file.write_fmt(format_args!("{}\n", "!_TAG_FILE_SORTED	1	/0=unsorted, 1=sorted, 2=foldcase/")));

         for line in merged_lines.iter() {
            try!(tag_file.write_fmt(format_args!("{}\n", *line)));
         }
      },

      TagsKind::Emacs => {
         let mut tag_file = try!(
             OpenOptions::new()
                .create(true)
                .append(true)
                .read(true)
                .write(true)
                .open(into_tag_file)
         );

         for file in tag_files.iter() {
            if file.as_path() != into_tag_file.as_path() {
               try!(tag_file.write_fmt(format_args!("{},include\n", file.display())));
            }
         }
      }
   }

   Ok(())
}

/// creates tags recursive for the directory hierarchy starting at `src_dir`
/// and writes them to `tags_file`
pub fn create_tags<P: AsPath>(tags_kind: &TagsKind, src_dir: &P, tags_file: &P) -> AppResult<()>
{
   let mut cmd = Command::new("ctags");

   tags_kind.ctags_option().map(|opt| { cmd.arg(opt); () });

   cmd.arg("--recurse")
      .arg("--languages=Rust")
      .arg("--langdef=Rust")
      .arg("--langmap=Rust:.rs")
      .arg("--regex-Rust=/^[ \\t]*(#\\[[^\\]]\\][ \\t]*)*(pub[ \\t]+)?(extern[ \\t]+)?(\"[^\"]+\"[ \\t]+)?(unsafe[ \\t]+)?fn[ \\t]+([a-zA-Z0-9_]+)/\\6/f,functions,function definitions/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?type[ \\t]+([a-zA-Z0-9_]+)/\\2/T,types,type definitions/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?enum[ \\t]+([a-zA-Z0-9_]+)/\\2/g,enum,enumeration names/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?struct[ \\t]+([a-zA-Z0-9_]+)/\\2/s,structure names/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?mod[ \\t]+([a-zA-Z0-9_]+)/\\2/m,modules,module names/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?static[ \\t]+([a-zA-Z0-9_]+)/\\2/c,consts,static constants/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?trait[ \\t]+([a-zA-Z0-9_]+)/\\2/t,traits,traits/")
      .arg("--regex-Rust=/^[ \\t]*(pub[ \\t]+)?impl([ \\t\\n]*<[^>]*>)?[ \\t]+(([a-zA-Z0-9_:]+)[ \\t]*(<[^>]*>)?[ \\t]+(for)[ \\t]+)?([a-zA-Z0-9_]+)/\\4 \\6 \\7/i,impls,trait implementations/")
      .arg("--regex-Rust=/^[ \\t]*macro_rules![ \\t]+([a-zA-Z0-9_]+)/\\1/d,macros,macro definitions/")
      .arg("-o")
      .arg(tags_file.as_path())
      .arg(src_dir.as_path());

   println!("Creating tags ...\n   for source:\n      {}\n\n   cached at:\n      {}\n",
            src_dir.as_path().display(), tags_file.as_path().display());

   try!(cmd.output());
   Ok(())
}

/// find the source directory of `source`, for git sources the directories
/// in `~/.cargo/git/checkouts` are considered and for crates.io sources
/// the directories in `~/.cargo/registry/src/github.com-*` are considered
fn find_src_dir(source: &SourceKind) -> AppResult<PathBuf>
{
   match *source {
      SourceKind::Git { ref lib_name, ref commit_hash } => {
         let mut lib_src = lib_name.clone();
         lib_src.push_str("-*");

         let mut src_dir = try!(cargo_git_src_dir());
         src_dir.push(&lib_src);
         src_dir.push("master");

         let src_paths = try!(glob_path(&format!("{}", src_dir.display())));
         for src_path in src_paths {
            if let Ok(path) = src_path {
               let src_path_buf = PathBuf::new(&format!("{}", path.display()));
               let src_commit_hash = try!(get_commit_hash(&src_path_buf));
               if *commit_hash == src_commit_hash {
                  return Ok(src_path_buf);
               }
            }
         }

         // the git repository name hasn't to match the name of the library,
         // so here we're just going through all git directories and searching
         // for the one with a matching commit hash
         let mut src_dir = try!(cargo_git_src_dir());
         src_dir.push("*");
         src_dir.push("master");

         let src_paths = try!(glob_path(&format!("{}", src_dir.display())));
         for src_path in src_paths {
            if let Ok(path) = src_path {
               let src_path_buf = PathBuf::new(&format!("{}", path.display()));
               let src_commit_hash = try!(get_commit_hash(&src_path_buf));
               if *commit_hash == src_commit_hash {
                  return Ok(src_path_buf);
               }
            }
         }

         Err(app_err(format!("
   Couldn't find git repository of the dependency '{}'!
   Have you run 'cargo build' at least once or have you added/updated a dependency without calling 'cargo build' again?", lib_name)))
      },

      SourceKind::CratesIo { ref lib_name, ref version } => {
         let mut lib_src = lib_name.clone();
         lib_src.push('-');
         lib_src.push_str(&**version);

         let mut src_dir = try!(cargo_crates_io_src_dir());
         src_dir.push(&lib_src);

         if ! src_dir.is_dir() {
            return Err(app_err(format!("
   Couldn't find source code of the dependency '{}'!
   Have you run 'cargo build' at least once or have you added/updated a dependency without calling 'cargo build' again?", lib_name)))
         }

         Ok(src_dir)
      }
   }
}

type CrateName = String;

/// searches in the file `<src_dir>/src/lib.rs` for external crates
/// that are reexpored and returns their names
fn find_reexported_crates<P: AsPath>(src_dir: &P) -> AppResult<Vec<CrateName>>
{
   let mut lib_file = src_dir.as_path().to_path_buf();
   lib_file.push("src");
   lib_file.push("lib.rs");

   if ! lib_file.is_file() {
      return Ok(Vec::new());
   }

   let contents = {
      let mut file = try!(File::open(&lib_file));
      let mut contents = String::new();
      try!(file.read_to_string(&mut contents));
      contents
   };

   let lines = contents.lines_any();

   type ModuleName = String;
   let mut pub_uses = HashSet::<ModuleName>::new();

   #[derive(Eq, PartialEq, Hash)]
   struct ExternCrate<'a>
   {
      name: &'a str,
      as_name: &'a str
   }

   let mut extern_crates = HashSet::<ExternCrate>::new();

   for line in lines {
      let items = line.trim_matches(';').split(' ').collect::<Vec<&str>>();
      if items.len() < 3 {
         continue;
      }

      if items[0] == "pub" && items[1] == "use" {
         let mods = items[2].split("::").collect::<Vec<&str>>();
         if mods.len() >= 1 {
            pub_uses.insert(mods[0].to_string());
         }
      }

      if items[0] == "extern" && items[1] == "crate" {
         if items.len() == 3 {
            extern_crates.insert(ExternCrate { name: items[2].trim_matches('"'), as_name: items[2] });
         }
         else if items.len() == 5 && items[3] == "as" {
            extern_crates.insert(ExternCrate { name: items[2].trim_matches('"'), as_name: items[4] });
         }
      }
   }

   let mut reexp_crates = Vec::<CrateName>::new();
   for extern_crate in extern_crates.iter() {
      if pub_uses.contains(extern_crate.as_name) {
         reexp_crates.push(extern_crate.name.to_string());
      }
   }

   Ok(reexp_crates)
}

/// get the commit hash of the current `HEAD` of the git repository located at `git_dir`
fn get_commit_hash<P: AsPath>(git_dir: &P) -> AppResult<String>
{
   let mut cmd = Command::new("git");
   cmd.current_dir(git_dir)
      .arg("rev-parse")
      .arg("HEAD");

   let out = try!(cmd.output());
   String::from_utf8(out.stdout)
      .map(|s| s.as_slice().trim().to_string())
      .map_err(|_| app_err("Couldn't convert git output to utf8!".to_string()))
}
<|endoftext|>


<commit_before>//!Constants and structures from include/uapi/linux/time.h

///A structure that contains the number of seconds and nanoseconds since an epoch.
///
///If in doubt, assume we're talking about the UNIX epoch.
pub struct timespec {
    ///The number of seconds contained in this timespec
    tv_sec: ::time_t,

    ///The number of nanoseconds contained in this timespec
    tv_nsec: ::c_long
}

///A structure that contains the number of seconds and microseconds since an epoch.
///
///If in doubt, assume we're talking about the UNIX epoch.
pub struct timeval {
    ///The number of seconds contained in this timeval
    tv_sec: ::time_t,

    ///The number of microseconds contained in this timeval
    tv_usec: ::suseconds_t
}

///A structure containing information on the time-based location of a timezone
///
///Please note that this does not include the name or country code, only the minutes west of Greenwich and the type of DST correction
pub struct timezone {
    ///The number of minutes west of Greenwich
    tz_minuteswest: ::c_int,

    ///The type of Daylight Savings Time correction
    tz_dsttime: ::c_int
}

//Names of the interval timers

///An interval timer that decrements in real time
///
///On expiration, a SIGALRM is delivered
pub const ITIMER_REAL: ::c_int = 0;

///An interval timer that decrements only when the process is executing.
///
///On expiration, a SIGVTALRM is delivered
pub const ITIMER_VIRTUAL: ::c_int = 1;

///Decrements both while the process is executing and while the system is executing on behalf of the process
///
///This is usually used to profile kernel-space and user-space concurrently.
///
///If coupled with ITIMER_VIRTUAL, you can separate the two values - What is left when ITIMER_VIRTUAL's value is removed is kernel time
pub const ITIMER_PROF: ::c_int = 2;

///An interval timer based on a `timespec`
pub struct itimerspec {
    ///The period of time this timer should run for (Need to verify)
    it_interval: timespec,

    ///The amount of time left until expiration (Need to verify)
    it_value: timespec
}

///An interval timer based on a `timeval`
pub struct itimerval {
    ///The period of time this timer should run for (Need to verify)
    it_interval: timeval,

    ///The amount of time left until expiration (Need to verify)
    it_value: timeval
}

///A system-wide clock that measures time from the "real world"
///
///This clock **is** affected by discontinuous jumps in system time, NTP, and user changes
pub const CLOCK_REALTIME: ::clockid_t = 0;

///A clock that measures monotonic time since an unspecified starting point
///
///Unless you manage to break your system, this unspecified point is usually when your computer powers on.
///
///This is not affected by user changes, but is by `adjtime` and NTP.
pub const CLOCK_MONOTONIC: ::clockid_t = 1;

///A high-resolution per-process timer from the processor.
pub const CLOCK_PROCESS_CPUTIME_ID: ::clockid_t = 2;

///A (high-resolution?) thread-specific timer from the processor
pub const CLOCK_THREAD_CPUTIME_ID: ::clockid_t = 3;

///A hardware-based version of `CLOCK_MONOTONIC` that is not subject to changes
pub const CLOCK_MONOTONIC_RAW: ::clockid_t = 4;

///A faster but less precise version of `CLOCK_REALTIME`, measuring time in the "real world"
pub const CLOCK_REALTIME_COARSE: ::clockid_t = 5;

///A faster but less precise version of `CLOCK_MONOTONIC`, measuring time since an unspecified starting point
pub const CLOCK_MONOTONIC_COARSE: ::clockid_t = 6;

///Identical to `CLOCK_MONOTONIC`, but includes any time that the system is suspended.
pub const CLOCK_BOOTIME: ::clockid_t = 7;

///Identical to `CLOCK_REALTIME`, but timers exposed via this will wake the system if suspended
pub const CLOCK_REALTIME_ALARM: ::clockid_t = 8;

///Identical to `CLOCK_BOOTIME`, but timers exposed via this will wake the system if suspended
pub const CLOCK_BOOTTIME_ALARM: ::clockid_t = 9;

///A clock used for SGI systems. Need to investigate
pub const CLOCK_SGI_CYCLE: ::clockid_t = 10;

///A clock that shows International Atomic Time
pub const CLOCK_TAI: ::clockid_t = 11;

///The maximum clock ID that the system is allowed to have
pub const MAX_CLOCKS: ::clockid_t = 16; //Resolves to c_int. Please let me know if this should be c_int on it's own

///A mask for supported clocks
///
///Needs to be investigated
pub const CLOCKS_MASK: ::clockid_t = CLOCK_REALTIME | CLOCK_MONOTONIC;

///A shorthand variant of CLOCK_MONOTONIC.
///
///This isn't used in the kernel. Is it left over from an old change that was reverted?
pub const CLOCKS_MONO: ::clockid_t = CLOCK_MONOTONIC;

///A flag indicating time is absolute
pub const TIMER_ABSTIME: ::c_int = 0x01;
<commit_msg>Allow time structs to be used in FFI<commit_after>//!Constants and structures from include/uapi/linux/time.h

///A structure that contains the number of seconds and nanoseconds since an epoch.
///
///If in doubt, assume we're talking about the UNIX epoch.
#[repr(C)]
pub struct timespec {
    ///The number of seconds contained in this timespec
    tv_sec: ::time_t,

    ///The number of nanoseconds contained in this timespec
    tv_nsec: ::c_long
}

///A structure that contains the number of seconds and microseconds since an epoch.
///
///If in doubt, assume we're talking about the UNIX epoch.
#[repr(C)]
pub struct timeval {
    ///The number of seconds contained in this timeval
    tv_sec: ::time_t,

    ///The number of microseconds contained in this timeval
    tv_usec: ::suseconds_t
}

///A structure containing information on the time-based location of a timezone
///
///Please note that this does not include the name or country code, only the minutes west of Greenwich and the type of DST correction
#[repr(C)]
pub struct timezone {
    ///The number of minutes west of Greenwich
    tz_minuteswest: ::c_int,

    ///The type of Daylight Savings Time correction
    tz_dsttime: ::c_int
}

//Names of the interval timers

///An interval timer that decrements in real time
///
///On expiration, a SIGALRM is delivered
pub const ITIMER_REAL: ::c_int = 0;

///An interval timer that decrements only when the process is executing.
///
///On expiration, a SIGVTALRM is delivered
pub const ITIMER_VIRTUAL: ::c_int = 1;

///Decrements both while the process is executing and while the system is executing on behalf of the process
///
///This is usually used to profile kernel-space and user-space concurrently.
///
///If coupled with ITIMER_VIRTUAL, you can separate the two values - What is left when ITIMER_VIRTUAL's value is removed is kernel time
pub const ITIMER_PROF: ::c_int = 2;

///An interval timer based on a `timespec`
#[repr(C)]
pub struct itimerspec {
    ///The period of time this timer should run for (Need to verify)
    it_interval: timespec,

    ///The amount of time left until expiration (Need to verify)
    it_value: timespec
}

///An interval timer based on a `timeval`
#[repr(C)]
pub struct itimerval {
    ///The period of time this timer should run for (Need to verify)
    it_interval: timeval,

    ///The amount of time left until expiration (Need to verify)
    it_value: timeval
}

///A system-wide clock that measures time from the "real world"
///
///This clock **is** affected by discontinuous jumps in system time, NTP, and user changes
pub const CLOCK_REALTIME: ::clockid_t = 0;

///A clock that measures monotonic time since an unspecified starting point
///
///Unless you manage to break your system, this unspecified point is usually when your computer powers on.
///
///This is not affected by user changes, but is by `adjtime` and NTP.
pub const CLOCK_MONOTONIC: ::clockid_t = 1;

///A high-resolution per-process timer from the processor.
pub const CLOCK_PROCESS_CPUTIME_ID: ::clockid_t = 2;

///A (high-resolution?) thread-specific timer from the processor
pub const CLOCK_THREAD_CPUTIME_ID: ::clockid_t = 3;

///A hardware-based version of `CLOCK_MONOTONIC` that is not subject to changes
pub const CLOCK_MONOTONIC_RAW: ::clockid_t = 4;

///A faster but less precise version of `CLOCK_REALTIME`, measuring time in the "real world"
pub const CLOCK_REALTIME_COARSE: ::clockid_t = 5;

///A faster but less precise version of `CLOCK_MONOTONIC`, measuring time since an unspecified starting point
pub const CLOCK_MONOTONIC_COARSE: ::clockid_t = 6;

///Identical to `CLOCK_MONOTONIC`, but includes any time that the system is suspended.
pub const CLOCK_BOOTIME: ::clockid_t = 7;

///Identical to `CLOCK_REALTIME`, but timers exposed via this will wake the system if suspended
pub const CLOCK_REALTIME_ALARM: ::clockid_t = 8;

///Identical to `CLOCK_BOOTIME`, but timers exposed via this will wake the system if suspended
pub const CLOCK_BOOTTIME_ALARM: ::clockid_t = 9;

///A clock used for SGI systems. Need to investigate
pub const CLOCK_SGI_CYCLE: ::clockid_t = 10;

///A clock that shows International Atomic Time
pub const CLOCK_TAI: ::clockid_t = 11;

///The maximum clock ID that the system is allowed to have
pub const MAX_CLOCKS: ::clockid_t = 16; //Resolves to c_int. Please let me know if this should be c_int on it's own

///A mask for supported clocks
///
///Needs to be investigated
pub const CLOCKS_MASK: ::clockid_t = CLOCK_REALTIME | CLOCK_MONOTONIC;

///A shorthand variant of CLOCK_MONOTONIC.
///
///This isn't used in the kernel. Is it left over from an old change that was reverted?
pub const CLOCKS_MONO: ::clockid_t = CLOCK_MONOTONIC;

///A flag indicating time is absolute
pub const TIMER_ABSTIME: ::c_int = 0x01;
<|endoftext|>


<commit_before>use std::fmt::Debug;
use std::hash::{Hash, Hasher, SipHasher};

use adapton::bitstring::*;
use adapton::engine::{Art, Name, force};

/// Probablistically Balanced Trie
/// Rough implementation of probabilistic tries from OOPSLA 2015 paper.
///
/// See also: [Tries in OCaml](http://github.com/plum-umd/adapton.ocaml)
#[derive(Debug,Clone)]
pub enum Trie<X> {
    Nil(BS),
    Leaf(BS, X),
    Bin(BS, Box<Trie<X>>, Box<Trie<X>>),
    Root(Meta, Box<Trie<X>>),
    Name(Name, Box<Trie<X>>),
    Art(Art<Trie<X>>),
}

#[derive(Debug,PartialEq,Eq,Clone)]
pub enum IFreq {
    Never,
    Depth(u64, u64),
    First(u64),
    Const(u64),
}

impl Hash for IFreq {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match *self {
            IFreq::Never => {
                "Adapton.Trie.Meta.Freq#Never".hash(state);
            }
            IFreq::Depth(i, j) => {
                "Adapton.Trie.Meta.Freq#Depth".hash(state);
                i.hash(state);
                j.hash(state);
            }
            IFreq::First(n) => {
                "Adapton.Trie.Meta.Freq#First".hash(state);
                n.hash(state);
            }
            IFreq::Const(i) => {
                "Adapton.Trie.Meta.Freq#Const".hash(state);
                i.hash(state);
            }
        }
    }
}

/// Metadata held by the root node.
#[derive(Debug,PartialEq,Eq,Hash,Clone)]
pub struct Meta {
    min_depth: u64,
    ifreq: IFreq,
}

pub trait MetaT {
    fn hash_seeded(&self, u64);
}

impl MetaT for Meta {
    fn hash_seeded(&self, seed: u64) {
        let mut hasher = SipHasher::new_with_keys(0, seed);
        "Adapton.Trie.Meta".hash(&mut hasher);
        self.min_depth.hash(&mut hasher);
        self.ifreq.hash(&mut hasher);
    }
}

pub trait TrieIntro<X>: Debug + Hash + PartialEq + Eq + Clone + 'static {
    fn nil() -> Self;
    fn leaf(BS, X) -> Self;
    fn bin(BS, Self, Self) -> Self;
    fn root(Meta, Self) -> Self;

    // requisite "adaptonic" constructors: `name` and `art`:
    fn name(Name, Self) -> Self;
    fn art(Art<Self>) -> Self;
}

pub trait TrieElim<X>: Debug + Hash + PartialEq + Eq + Clone + 'static {
    fn find(Self, i64) -> Option<X>;

    fn elim<Res, NilC, LeafC, BinC, RootC, NameC>(Self, NilC, LeafC, BinC, RootC, NameC) -> Res
        where NilC: FnOnce(BS) -> Res,
              LeafC: FnOnce(BS, X) -> Res,
              BinC: FnOnce(BS, Self, Self) -> Res,
              RootC: FnOnce(Meta, Self) -> Res,
              NameC: FnOnce(Name, Self) -> Res;

    fn elim_ref<Res, NilC, LeafC, BinC, RootC, NameC>(&Self,
                                                      NilC,
                                                      LeafC,
                                                      BinC,
                                                      RootC,
                                                      NameC)
                                                      -> Res
        where NilC: FnOnce(&BS) -> Res,
              LeafC: FnOnce(&BS, &X) -> Res,
              BinC: FnOnce(&BS, &Self, &Self) -> Res,
              RootC: FnOnce(&Meta, &Self) -> Res,
              NameC: FnOnce(&Name, &Self) -> Res;
}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> TrieIntro<X> for Trie<X> {
    fn nil() -> Self {
        Trie::Nil(BS {
            length: 0,
            value: 0,
        })
    }
    fn leaf(bs: BS, x: X) -> Self {
        Trie::Leaf(bs, x)
    }
    fn bin(bs: BS, l: Self, r: Self) -> Self {
        Trie::Bin(bs, Box::new(l), Box::new(r))
    }
    fn root(meta: Meta, trie: Self) -> Self {
        Trie::Root(meta, Box::new(trie))
    }
    fn name(nm: Name, trie: Self) -> Self {
        Trie::Name(nm, Box::new(trie))
    }
    fn art(art: Art<Self>) -> Self {
        Trie::Art(art)
    }
}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> PartialEq for Trie<X> {
    fn eq(&self, other: &Trie<X>) -> bool {
        match (self, other) {
            (&Trie::Nil(bs_self), &Trie::Nil(bs_other)) => bs_self == bs_other,
            (&Trie::Leaf(bs_self, ref e_self), &Trie::Leaf(bs_other, ref e_other)) => {
                let bs_equal = bs_self == bs_other;
                bs_equal && e_self == e_other
            }
            (&Trie::Bin(bs, ref left, ref right),
             &Trie::Bin(bs_other, ref left_other, ref right_other)) => {
                bs == bs_other && left == left_other && right == right_other
            }
            (&Trie::Root(ref md, ref t), &Trie::Root(ref md_other, ref t_other)) => {
                md == md_other && t == t_other
            }
            (&Trie::Name(ref nm, ref t), &Trie::Name(ref nm_other, ref t_other)) => {
                nm == nm_other && t == t_other
            }
            _ => false,
        }
    }
}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> Eq for Trie<X> {}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> Hash for Trie<X> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match *self {
            Trie::Nil(bs) => bs.hash(state),
            Trie::Leaf(bs, ref x) => {
                x.hash(state);
                bs.hash(state)
            }
            Trie::Bin(bs, ref left, ref right) => {
                right.hash(state);
                left.hash(state);
                bs.hash(state)
            }
            Trie::Root(ref md, ref t) => {
                t.hash(state);
                md.hash_seeded(state.finish());
            }
            Trie::Name(ref nm, ref t) => {
                t.hash(state);
                nm.hash(state)
            }
            Trie::Art(ref art_t) => art_t.hash(state),
        }
    }
}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> TrieElim<X> for Trie<X> {
    fn find(trie: Self, i: i64) -> Option<X> {
        fn _loop<X: Debug + Hash + PartialEq + Eq + Clone + 'static>(h: i64,
                                                                     t: Trie<X>)
                                                                     -> Option<X> {
            match t {
                Trie::Nil(_) => None,
                Trie::Leaf(_, x) => Some(x),
                Trie::Bin(_, left, right) => _loop(h >> 1, if h % 2 == 0 { *left } else { *right }),
                Trie::Root(_, t) => _loop(h, *t),
                Trie::Name(_, t) => _loop(h, *t),
                Trie::Art(art_t) => _loop(h, force(&art_t)),
            }
        };
        _loop(i, trie)
    }

    fn elim<Res, NilC, LeafC, BinC, RootC, NameC>(trie: Self,
                                                  nil: NilC,
                                                  leaf: LeafC,
                                                  bin: BinC,
                                                  root: RootC,
                                                  name: NameC)
                                                  -> Res
        where NilC: FnOnce(BS) -> Res,
              LeafC: FnOnce(BS, X) -> Res,
              BinC: FnOnce(BS, Self, Self) -> Res,
              RootC: FnOnce(Meta, Self) -> Res,
              NameC: FnOnce(Name, Self) -> Res
    {
        match trie {
            Trie::Nil(bs) => nil(bs),
            Trie::Leaf(bs, x) => leaf(bs, x),
            Trie::Bin(bs, l, r) => bin(bs, *l, *r),
            Trie::Name(nm, t) => name(nm, *t),
            Trie::Root(meta, t) => root(meta, *t),
            Trie::Art(art) => {
                let trie = force(&art);
                Self::elim(trie, nil, leaf, bin, root, name)
            }
        }
    }

    fn elim_ref<Res, NilC, LeafC, BinC, RootC, NameC>(trie: &Self,
                                                      nil: NilC,
                                                      leaf: LeafC,
                                                      bin: BinC,
                                                      root: RootC,
                                                      name: NameC)
                                                      -> Res
        where NilC: FnOnce(&BS) -> Res,
              LeafC: FnOnce(&BS, &X) -> Res,
              BinC: FnOnce(&BS, &Self, &Self) -> Res,
              RootC: FnOnce(&Meta, &Self) -> Res,
              NameC: FnOnce(&Name, &Self) -> Res
    {
        match *trie {
            Trie::Nil(ref bs) => nil(bs),
            Trie::Leaf(ref bs, ref x) => leaf(bs, x),
            Trie::Bin(ref bs, ref l, ref r) => bin(bs, &*l, &*r),
            Trie::Name(ref nm, ref t) => name(nm, &*t),
            Trie::Root(ref meta, ref t) => root(meta, &*t),
            Trie::Art(ref art) => {
                let trie = force(&art);
                Self::elim_ref(&trie, nil, leaf, bin, root, name)
            }
        }
    }
}
<commit_msg>Implement find on tries with elim_ref<commit_after>use std::fmt::Debug;
use std::hash::{Hash, Hasher, SipHasher};

use adapton::bitstring::*;
use adapton::engine::{Art, Name, force};

/// Probablistically Balanced Trie
/// Rough implementation of probabilistic tries from OOPSLA 2015 paper.
///
/// See also: [Tries in OCaml](http://github.com/plum-umd/adapton.ocaml)
#[derive(Debug,Clone)]
pub enum Trie<X> {
    Nil(BS),
    Leaf(BS, X),
    Bin(BS, Box<Trie<X>>, Box<Trie<X>>),
    Root(Meta, Box<Trie<X>>),
    Name(Name, Box<Trie<X>>),
    Art(Art<Trie<X>>),
}

#[derive(Debug,PartialEq,Eq,Clone)]
pub enum IFreq {
    Never,
    Depth(u64, u64),
    First(u64),
    Const(u64),
}

impl Hash for IFreq {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match *self {
            IFreq::Never => {
                "Adapton.Trie.Meta.Freq#Never".hash(state);
            }
            IFreq::Depth(i, j) => {
                "Adapton.Trie.Meta.Freq#Depth".hash(state);
                i.hash(state);
                j.hash(state);
            }
            IFreq::First(n) => {
                "Adapton.Trie.Meta.Freq#First".hash(state);
                n.hash(state);
            }
            IFreq::Const(i) => {
                "Adapton.Trie.Meta.Freq#Const".hash(state);
                i.hash(state);
            }
        }
    }
}

/// Metadata held by the root node.
#[derive(Debug,PartialEq,Eq,Hash,Clone)]
pub struct Meta {
    min_depth: u64,
    ifreq: IFreq,
}

pub trait MetaT {
    fn hash_seeded(&self, u64);
}

impl MetaT for Meta {
    fn hash_seeded(&self, seed: u64) {
        let mut hasher = SipHasher::new_with_keys(0, seed);
        "Adapton.Trie.Meta".hash(&mut hasher);
        self.min_depth.hash(&mut hasher);
        self.ifreq.hash(&mut hasher);
    }
}

pub trait TrieIntro<X>: Debug + Hash + PartialEq + Eq + Clone + 'static {
    fn nil() -> Self;
    fn leaf(BS, X) -> Self;
    fn bin(BS, Self, Self) -> Self;
    fn root(Meta, Self) -> Self;

    // requisite "adaptonic" constructors: `name` and `art`:
    fn name(Name, Self) -> Self;
    fn art(Art<Self>) -> Self;
}

pub trait TrieElim<X>: Debug + Hash + PartialEq + Eq + Clone + 'static {
    fn find(&Self, i64) -> Option<X>;

    fn elim<Res, NilC, LeafC, BinC, RootC, NameC>(Self, NilC, LeafC, BinC, RootC, NameC) -> Res
        where NilC: FnOnce(BS) -> Res,
              LeafC: FnOnce(BS, X) -> Res,
              BinC: FnOnce(BS, Self, Self) -> Res,
              RootC: FnOnce(Meta, Self) -> Res,
              NameC: FnOnce(Name, Self) -> Res;

    fn elim_ref<Res, NilC, LeafC, BinC, RootC, NameC>(&Self,
                                                      NilC,
                                                      LeafC,
                                                      BinC,
                                                      RootC,
                                                      NameC)
                                                      -> Res
        where NilC: FnOnce(&BS) -> Res,
              LeafC: FnOnce(&BS, &X) -> Res,
              BinC: FnOnce(&BS, &Self, &Self) -> Res,
              RootC: FnOnce(&Meta, &Self) -> Res,
              NameC: FnOnce(&Name, &Self) -> Res;
}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> TrieIntro<X> for Trie<X> {
    fn nil() -> Self {
        Trie::Nil(BS {
            length: 0,
            value: 0,
        })
    }
    fn leaf(bs: BS, x: X) -> Self {
        Trie::Leaf(bs, x)
    }
    fn bin(bs: BS, l: Self, r: Self) -> Self {
        Trie::Bin(bs, Box::new(l), Box::new(r))
    }
    fn root(meta: Meta, trie: Self) -> Self {
        Trie::Root(meta, Box::new(trie))
    }
    fn name(nm: Name, trie: Self) -> Self {
        Trie::Name(nm, Box::new(trie))
    }
    fn art(art: Art<Self>) -> Self {
        Trie::Art(art)
    }
}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> PartialEq for Trie<X> {
    fn eq(&self, other: &Trie<X>) -> bool {
        match (self, other) {
            (&Trie::Nil(bs_self), &Trie::Nil(bs_other)) => bs_self == bs_other,
            (&Trie::Leaf(bs_self, ref e_self), &Trie::Leaf(bs_other, ref e_other)) => {
                let bs_equal = bs_self == bs_other;
                bs_equal && e_self == e_other
            }
            (&Trie::Bin(bs, ref left, ref right),
             &Trie::Bin(bs_other, ref left_other, ref right_other)) => {
                bs == bs_other && left == left_other && right == right_other
            }
            (&Trie::Root(ref md, ref t), &Trie::Root(ref md_other, ref t_other)) => {
                md == md_other && t == t_other
            }
            (&Trie::Name(ref nm, ref t), &Trie::Name(ref nm_other, ref t_other)) => {
                nm == nm_other && t == t_other
            }
            _ => false,
        }
    }
}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> Eq for Trie<X> {}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> Hash for Trie<X> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match *self {
            Trie::Nil(bs) => bs.hash(state),
            Trie::Leaf(bs, ref x) => {
                x.hash(state);
                bs.hash(state)
            }
            Trie::Bin(bs, ref left, ref right) => {
                right.hash(state);
                left.hash(state);
                bs.hash(state)
            }
            Trie::Root(ref md, ref t) => {
                t.hash(state);
                md.hash_seeded(state.finish());
            }
            Trie::Name(ref nm, ref t) => {
                t.hash(state);
                nm.hash(state)
            }
            Trie::Art(ref art_t) => art_t.hash(state),
        }
    }
}

impl<X: Debug + Hash + PartialEq + Eq + Clone + 'static> TrieElim<X> for Trie<X> {
    fn find(trie: &Self, i: i64) -> Option<X> {
        Self::elim_ref(trie,
                       |_| None,
                       |_, x| Some(x.clone()),
                       |_, left, right| if i % 2 == 0 {
                           Self::find(left, i >> 1)
                       } else {
                           Self::find(right, i >> 1)
                       },
                       |_, t| Self::find(t, i),
                       |_, t| Self::find(t, i))
    }

    fn elim<Res, NilC, LeafC, BinC, RootC, NameC>(trie: Self,
                                                  nil: NilC,
                                                  leaf: LeafC,
                                                  bin: BinC,
                                                  root: RootC,
                                                  name: NameC)
                                                  -> Res
        where NilC: FnOnce(BS) -> Res,
              LeafC: FnOnce(BS, X) -> Res,
              BinC: FnOnce(BS, Self, Self) -> Res,
              RootC: FnOnce(Meta, Self) -> Res,
              NameC: FnOnce(Name, Self) -> Res
    {
        match trie {
            Trie::Nil(bs) => nil(bs),
            Trie::Leaf(bs, x) => leaf(bs, x),
            Trie::Bin(bs, l, r) => bin(bs, *l, *r),
            Trie::Name(nm, t) => name(nm, *t),
            Trie::Root(meta, t) => root(meta, *t),
            Trie::Art(art) => {
                let trie = force(&art);
                Self::elim(trie, nil, leaf, bin, root, name)
            }
        }
    }

    fn elim_ref<Res, NilC, LeafC, BinC, RootC, NameC>(trie: &Self,
                                                      nil: NilC,
                                                      leaf: LeafC,
                                                      bin: BinC,
                                                      root: RootC,
                                                      name: NameC)
                                                      -> Res
        where NilC: FnOnce(&BS) -> Res,
              LeafC: FnOnce(&BS, &X) -> Res,
              BinC: FnOnce(&BS, &Self, &Self) -> Res,
              RootC: FnOnce(&Meta, &Self) -> Res,
              NameC: FnOnce(&Name, &Self) -> Res
    {
        match *trie {
            Trie::Nil(ref bs) => nil(bs),
            Trie::Leaf(ref bs, ref x) => leaf(bs, x),
            Trie::Bin(ref bs, ref l, ref r) => bin(bs, &*l, &*r),
            Trie::Name(ref nm, ref t) => name(nm, &*t),
            Trie::Root(ref meta, ref t) => root(meta, &*t),
            Trie::Art(ref art) => {
                let trie = force(art);
                Self::elim_ref(&trie, nil, leaf, bin, root, name)
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Undo API
use std::fmt::Debug;

use crate::keymap::RepeatCount;
use crate::line_buffer::{ChangeListener, DeleteListener, Direction, LineBuffer};
use unicode_segmentation::UnicodeSegmentation;

enum Change {
    Begin,
    End,
    Insert {
        idx: usize,
        text: String,
    }, // QuotedInsert, SelfInsert, Yank
    Delete {
        idx: usize,
        text: String,
    }, /* BackwardDeleteChar, BackwardKillWord, DeleteChar,
        * KillLine, KillWholeLine, KillWord,
        * UnixLikeDiscard, ViDeleteTo */
    Replace {
        idx: usize,
        old: String,
        new: String,
    }, /* CapitalizeWord, Complete, DowncaseWord, Replace, TransposeChars, TransposeWords,
        * UpcaseWord, YankPop */
}

impl Change {
    fn undo(&self, line: &mut LineBuffer) {
        match *self {
            Change::Begin | Change::End => {
                unreachable!();
            }
            Change::Insert { idx, ref text } => {
                line.delete_range(idx..idx + text.len());
            }
            Change::Delete { idx, ref text } => {
                line.insert_str(idx, text);
                line.set_pos(idx + text.len());
            }
            Change::Replace {
                idx,
                ref old,
                ref new,
            } => {
                line.replace(idx..idx + new.len(), old);
            }
        }
    }

    #[cfg(test)]
    fn redo(&self, line: &mut LineBuffer) {
        match *self {
            Change::Begin | Change::End => {
                unreachable!();
            }
            Change::Insert { idx, ref text } => {
                line.insert_str(idx, text);
            }
            Change::Delete { idx, ref text } => {
                line.delete_range(idx..idx + text.len());
            }
            Change::Replace {
                idx,
                ref old,
                ref new,
            } => {
                line.replace(idx..idx + old.len(), new);
            }
        }
    }

    fn insert_seq(&self, indx: usize) -> bool {
        if let Change::Insert { idx, ref text } = *self {
            idx + text.len() == indx
        } else {
            false
        }
    }

    fn delete_seq(&self, indx: usize, len: usize) -> bool {
        if let Change::Delete { idx, .. } = *self {
            // delete or backspace
            idx == indx || idx == indx + len
        } else {
            false
        }
    }

    fn replace_seq(&self, indx: usize) -> bool {
        if let Change::Replace { idx, ref new, .. } = *self {
            idx + new.len() == indx
        } else {
            false
        }
    }
}

pub struct Changeset {
    undo_group_level: u32,
    undos: Vec<Change>, // undoable changes
    redos: Vec<Change>, // undone changes, redoable
}

impl Changeset {
    pub fn new() -> Self {
        Self {
            undo_group_level: 0,
            undos: Vec::new(),
            redos: Vec::new(),
        }
    }

    pub fn begin(&mut self) -> usize {
        debug!(target: "rustyline", "Changeset::begin");
        self.redos.clear();
        let mark = self.undos.len();
        self.undos.push(Change::Begin);
        self.undo_group_level += 1;
        mark
    }

    pub fn end(&mut self) {
        debug!(target: "rustyline", "Changeset::end");
        self.redos.clear();
        while self.undo_group_level > 0 {
            self.undo_group_level -= 1;
            if let Some(&Change::Begin) = self.undos.last() {
                // empty Begin..End
                self.undos.pop();
            } else {
                self.undos.push(Change::End);
            }
        }
    }

    fn insert_char(idx: usize, c: char) -> Change {
        let mut text = String::new();
        text.push(c);
        Change::Insert { idx, text }
    }

    pub fn insert(&mut self, idx: usize, c: char) {
        debug!(target: "rustyline", "Changeset::insert({}, {:?})", idx, c);
        self.redos.clear();
        if !c.is_alphanumeric() || !self.undos.last().map_or(false, |lc| lc.insert_seq(idx)) {
            self.undos.push(Self::insert_char(idx, c));
            return;
        }
        // merge consecutive char insertions when char is alphanumeric
        let mut last_change = self.undos.pop().unwrap();
        if let Change::Insert { ref mut text, .. } = last_change {
            text.push(c);
        } else {
            unreachable!();
        }
        self.undos.push(last_change);
    }

    pub fn insert_str<S: AsRef<str> + Into<String> + Debug>(&mut self, idx: usize, string: S) {
        debug!(target: "rustyline", "Changeset::insert_str({}, {:?})", idx, string);
        self.redos.clear();
        if string.as_ref().is_empty() {
            return;
        }
        self.undos.push(Change::Insert {
            idx,
            text: string.into(),
        });
    }

    pub fn delete<S: AsRef<str> + Into<String> + Debug>(&mut self, indx: usize, string: S) {
        debug!(target: "rustyline", "Changeset::delete({}, {:?})", indx, string);
        self.redos.clear();
        if string.as_ref().is_empty() {
            return;
        }

        if !Self::single_char(string.as_ref())
            || !self
                .undos
                .last()
                .map_or(false, |lc| lc.delete_seq(indx, string.as_ref().len()))
        {
            self.undos.push(Change::Delete {
                idx: indx,
                text: string.into(),
            });
            return;
        }
        // merge consecutive char deletions when char is alphanumeric
        let mut last_change = self.undos.pop().unwrap();
        if let Change::Delete {
            ref mut idx,
            ref mut text,
        } = last_change
        {
            if *idx == indx {
                text.push_str(string.as_ref());
            } else {
                text.insert_str(0, string.as_ref());
                *idx = indx;
            }
        } else {
            unreachable!();
        }
        self.undos.push(last_change);
    }

    fn single_char(s: &str) -> bool {
        let mut graphemes = s.graphemes(true);
        graphemes.next().map_or(false, |grapheme| {
            grapheme.chars().all(char::is_alphanumeric)
        }) && graphemes.next().is_none()
    }

    pub fn replace<S: AsRef<str> + Into<String> + Debug>(&mut self, indx: usize, old_: S, new_: S) {
        debug!(target: "rustyline", "Changeset::replace({}, {:?}, {:?})", indx, old_, new_);
        self.redos.clear();

        if !self.undos.last().map_or(false, |lc| lc.replace_seq(indx)) {
            self.undos.push(Change::Replace {
                idx: indx,
                old: old_.into(),
                new: new_.into(),
            });
            return;
        }

        // merge consecutive char replacements
        let mut last_change = self.undos.pop().unwrap();
        if let Change::Replace {
            ref mut old,
            ref mut new,
            ..
        } = last_change
        {
            old.push_str(old_.as_ref());
            new.push_str(new_.as_ref());
        } else {
            unreachable!();
        }
        self.undos.push(last_change);
    }

    pub fn undo(&mut self, line: &mut LineBuffer, n: RepeatCount) -> bool {
        debug!(target: "rustyline", "Changeset::undo");
        let mut count = 0;
        let mut waiting_for_begin = 0;
        let mut undone = false;
        loop {
            if let Some(change) = self.undos.pop() {
                match change {
                    Change::Begin => {
                        waiting_for_begin -= 1;
                    }
                    Change::End => {
                        waiting_for_begin += 1;
                    }
                    _ => {
                        change.undo(line);
                        undone = true;
                    }
                };
                self.redos.push(change);
            } else {
                break;
            }
            if waiting_for_begin <= 0 {
                count += 1;
                if count >= n {
                    break;
                }
            }
        }
        undone
    }

    pub fn truncate(&mut self, len: usize) {
        debug!(target: "rustyline", "Changeset::truncate({})", len);
        self.undos.truncate(len);
    }

    #[cfg(test)]
    pub fn redo(&mut self, line: &mut LineBuffer) -> bool {
        let mut waiting_for_end = 0;
        let mut redone = false;
        loop {
            if let Some(change) = self.redos.pop() {
                match change {
                    Change::Begin => {
                        waiting_for_end += 1;
                    }
                    Change::End => {
                        waiting_for_end -= 1;
                    }
                    _ => {
                        change.redo(line);
                        redone = true;
                    }
                };
                self.undos.push(change);
            } else {
                break;
            }
            if waiting_for_end <= 0 {
                break;
            }
        }
        redone
    }

    pub fn last_insert(&self) -> Option<String> {
        for change in self.undos.iter().rev() {
            match change {
                Change::Insert { ref text, .. } => return Some(text.to_owned()),
                Change::Replace { ref new, .. } => return Some(new.to_owned()),
                Change::End => {
                    continue;
                }
                _ => {
                    return None;
                }
            }
        }
        None
    }
}

impl DeleteListener for Changeset {
    fn start_killing(&mut self) {}

    fn delete(&mut self, idx: usize, string: &str, _: Direction) {
        self.delete(idx, string);
    }

    fn stop_killing(&mut self) {}
}
impl ChangeListener for Changeset {
    fn insert_char(&mut self, idx: usize, c: char) {
        self.insert(idx, c);
    }

    fn insert_str(&mut self, idx: usize, string: &str) {
        self.insert_str(idx, string);
    }

    fn replace(&mut self, idx: usize, old: &str, new: &str) {
        self.replace(idx, old, new);
    }
}

#[cfg(test)]
mod tests {
    use super::Changeset;
    use crate::line_buffer::LineBuffer;

    #[test]
    fn test_insert_chars() {
        let mut cs = Changeset::new();
        cs.insert(0, 'H');
        cs.insert(1, 'i');
        assert_eq!(1, cs.undos.len());
        assert_eq!(0, cs.redos.len());
        cs.insert(0, ' ');
        assert_eq!(2, cs.undos.len());
    }

    #[test]
    fn test_insert_strings() {
        let mut cs = Changeset::new();
        cs.insert_str(0, "Hello");
        cs.insert_str(5, ", ");
        assert_eq!(2, cs.undos.len());
        assert_eq!(0, cs.redos.len());
    }

    #[test]
    fn test_undo_insert() {
        let mut buf = LineBuffer::init("", 0, None);
        buf.insert_str(0, "Hello");
        buf.insert_str(5, ", world!");
        let mut cs = Changeset::new();
        assert_eq!(buf.as_str(), "Hello, world!");

        cs.insert_str(5, ", world!");

        cs.undo(&mut buf, 1);
        assert_eq!(0, cs.undos.len());
        assert_eq!(1, cs.redos.len());
        assert_eq!(buf.as_str(), "Hello");

        cs.redo(&mut buf);
        assert_eq!(1, cs.undos.len());
        assert_eq!(0, cs.redos.len());
        assert_eq!(buf.as_str(), "Hello, world!");
    }

    #[test]
    fn test_undo_delete() {
        let mut buf = LineBuffer::init("", 0, None);
        buf.insert_str(0, "Hello");
        let mut cs = Changeset::new();
        assert_eq!(buf.as_str(), "Hello");

        cs.delete(5, ", world!");

        cs.undo(&mut buf, 1);
        assert_eq!(buf.as_str(), "Hello, world!");

        cs.redo(&mut buf);
        assert_eq!(buf.as_str(), "Hello");
    }

    #[test]
    fn test_delete_chars() {
        let mut buf = LineBuffer::init("", 0, None);
        buf.insert_str(0, "Hlo");

        let mut cs = Changeset::new();
        cs.delete(1, "e");
        cs.delete(1, "l");
        assert_eq!(1, cs.undos.len());

        cs.undo(&mut buf, 1);
        assert_eq!(buf.as_str(), "Hello");
    }

    #[test]
    fn test_backspace_chars() {
        let mut buf = LineBuffer::init("", 0, None);
        buf.insert_str(0, "Hlo");

        let mut cs = Changeset::new();
        cs.delete(2, "l");
        cs.delete(1, "e");
        assert_eq!(1, cs.undos.len());

        cs.undo(&mut buf, 1);
        assert_eq!(buf.as_str(), "Hello");
    }

    #[test]
    fn test_undo_replace() {
        let mut buf = LineBuffer::init("", 0, None);
        buf.insert_str(0, "Hello, world!");
        let mut cs = Changeset::new();
        assert_eq!(buf.as_str(), "Hello, world!");

        buf.replace(1..5, "i");
        assert_eq!(buf.as_str(), "Hi, world!");
        cs.replace(1, "ello", "i");

        cs.undo(&mut buf, 1);
        assert_eq!(buf.as_str(), "Hello, world!");

        cs.redo(&mut buf);
        assert_eq!(buf.as_str(), "Hi, world!");
    }

    #[test]
    fn test_last_insert() {
        let mut cs = Changeset::new();
        cs.begin();
        cs.delete(0, "Hello");
        cs.insert_str(0, "Bye");
        cs.end();
        let insert = cs.last_insert();
        assert_eq!(Some("Bye".to_owned()), insert);
    }
}
<commit_msg>Make possible to detect changes<commit_after>//! Undo API
use std::fmt::Debug;

use crate::keymap::RepeatCount;
use crate::line_buffer::{ChangeListener, DeleteListener, Direction, LineBuffer};
use unicode_segmentation::UnicodeSegmentation;

enum Change {
    Begin,
    End,
    Insert {
        idx: usize,
        text: String,
    }, // QuotedInsert, SelfInsert, Yank
    Delete {
        idx: usize,
        text: String,
    }, /* BackwardDeleteChar, BackwardKillWord, DeleteChar,
        * KillLine, KillWholeLine, KillWord,
        * UnixLikeDiscard, ViDeleteTo */
    Replace {
        idx: usize,
        old: String,
        new: String,
    }, /* CapitalizeWord, Complete, DowncaseWord, Replace, TransposeChars, TransposeWords,
        * UpcaseWord, YankPop */
}

impl Change {
    fn undo(&self, line: &mut LineBuffer) {
        match *self {
            Change::Begin | Change::End => {
                unreachable!();
            }
            Change::Insert { idx, ref text } => {
                line.delete_range(idx..idx + text.len());
            }
            Change::Delete { idx, ref text } => {
                line.insert_str(idx, text);
                line.set_pos(idx + text.len());
            }
            Change::Replace {
                idx,
                ref old,
                ref new,
            } => {
                line.replace(idx..idx + new.len(), old);
            }
        }
    }

    #[cfg(test)]
    fn redo(&self, line: &mut LineBuffer) {
        match *self {
            Change::Begin | Change::End => {
                unreachable!();
            }
            Change::Insert { idx, ref text } => {
                line.insert_str(idx, text);
            }
            Change::Delete { idx, ref text } => {
                line.delete_range(idx..idx + text.len());
            }
            Change::Replace {
                idx,
                ref old,
                ref new,
            } => {
                line.replace(idx..idx + old.len(), new);
            }
        }
    }

    fn insert_seq(&self, indx: usize) -> bool {
        if let Change::Insert { idx, ref text } = *self {
            idx + text.len() == indx
        } else {
            false
        }
    }

    fn delete_seq(&self, indx: usize, len: usize) -> bool {
        if let Change::Delete { idx, .. } = *self {
            // delete or backspace
            idx == indx || idx == indx + len
        } else {
            false
        }
    }

    fn replace_seq(&self, indx: usize) -> bool {
        if let Change::Replace { idx, ref new, .. } = *self {
            idx + new.len() == indx
        } else {
            false
        }
    }
}

pub struct Changeset {
    undo_group_level: u32,
    undos: Vec<Change>, // undoable changes
    redos: Vec<Change>, // undone changes, redoable
}

impl Changeset {
    pub fn new() -> Self {
        Self {
            undo_group_level: 0,
            undos: Vec::new(),
            redos: Vec::new(),
        }
    }

    pub fn begin(&mut self) -> usize {
        debug!(target: "rustyline", "Changeset::begin");
        self.redos.clear();
        let mark = self.undos.len();
        self.undos.push(Change::Begin);
        self.undo_group_level += 1;
        mark
    }

    /// Returns `true` when changes happen between the last call to `begin` and this `end`.
    pub fn end(&mut self) -> bool {
        debug!(target: "rustyline", "Changeset::end");
        self.redos.clear();
        let mut touched = false;
        while self.undo_group_level > 0 {
            self.undo_group_level -= 1;
            if let Some(&Change::Begin) = self.undos.last() {
                // empty Begin..End
                self.undos.pop();
            } else {
                self.undos.push(Change::End);
                touched = true;
            }
        }
        touched
    }

    fn insert_char(idx: usize, c: char) -> Change {
        let mut text = String::new();
        text.push(c);
        Change::Insert { idx, text }
    }

    pub fn insert(&mut self, idx: usize, c: char) {
        debug!(target: "rustyline", "Changeset::insert({}, {:?})", idx, c);
        self.redos.clear();
        if !c.is_alphanumeric() || !self.undos.last().map_or(false, |lc| lc.insert_seq(idx)) {
            self.undos.push(Self::insert_char(idx, c));
            return;
        }
        // merge consecutive char insertions when char is alphanumeric
        let mut last_change = self.undos.pop().unwrap();
        if let Change::Insert { ref mut text, .. } = last_change {
            text.push(c);
        } else {
            unreachable!();
        }
        self.undos.push(last_change);
    }

    pub fn insert_str<S: AsRef<str> + Into<String> + Debug>(&mut self, idx: usize, string: S) {
        debug!(target: "rustyline", "Changeset::insert_str({}, {:?})", idx, string);
        self.redos.clear();
        if string.as_ref().is_empty() {
            return;
        }
        self.undos.push(Change::Insert {
            idx,
            text: string.into(),
        });
    }

    pub fn delete<S: AsRef<str> + Into<String> + Debug>(&mut self, indx: usize, string: S) {
        debug!(target: "rustyline", "Changeset::delete({}, {:?})", indx, string);
        self.redos.clear();
        if string.as_ref().is_empty() {
            return;
        }

        if !Self::single_char(string.as_ref())
            || !self
                .undos
                .last()
                .map_or(false, |lc| lc.delete_seq(indx, string.as_ref().len()))
        {
            self.undos.push(Change::Delete {
                idx: indx,
                text: string.into(),
            });
            return;
        }
        // merge consecutive char deletions when char is alphanumeric
        let mut last_change = self.undos.pop().unwrap();
        if let Change::Delete {
            ref mut idx,
            ref mut text,
        } = last_change
        {
            if *idx == indx {
                text.push_str(string.as_ref());
            } else {
                text.insert_str(0, string.as_ref());
                *idx = indx;
            }
        } else {
            unreachable!();
        }
        self.undos.push(last_change);
    }

    fn single_char(s: &str) -> bool {
        let mut graphemes = s.graphemes(true);
        graphemes.next().map_or(false, |grapheme| {
            grapheme.chars().all(char::is_alphanumeric)
        }) && graphemes.next().is_none()
    }

    pub fn replace<S: AsRef<str> + Into<String> + Debug>(&mut self, indx: usize, old_: S, new_: S) {
        debug!(target: "rustyline", "Changeset::replace({}, {:?}, {:?})", indx, old_, new_);
        self.redos.clear();

        if !self.undos.last().map_or(false, |lc| lc.replace_seq(indx)) {
            self.undos.push(Change::Replace {
                idx: indx,
                old: old_.into(),
                new: new_.into(),
            });
            return;
        }

        // merge consecutive char replacements
        let mut last_change = self.undos.pop().unwrap();
        if let Change::Replace {
            ref mut old,
            ref mut new,
            ..
        } = last_change
        {
            old.push_str(old_.as_ref());
            new.push_str(new_.as_ref());
        } else {
            unreachable!();
        }
        self.undos.push(last_change);
    }

    pub fn undo(&mut self, line: &mut LineBuffer, n: RepeatCount) -> bool {
        debug!(target: "rustyline", "Changeset::undo");
        let mut count = 0;
        let mut waiting_for_begin = 0;
        let mut undone = false;
        loop {
            if let Some(change) = self.undos.pop() {
                match change {
                    Change::Begin => {
                        waiting_for_begin -= 1;
                    }
                    Change::End => {
                        waiting_for_begin += 1;
                    }
                    _ => {
                        change.undo(line);
                        undone = true;
                    }
                };
                self.redos.push(change);
            } else {
                break;
            }
            if waiting_for_begin <= 0 {
                count += 1;
                if count >= n {
                    break;
                }
            }
        }
        undone
    }

    pub fn truncate(&mut self, len: usize) {
        debug!(target: "rustyline", "Changeset::truncate({})", len);
        self.undos.truncate(len);
    }

    #[cfg(test)]
    pub fn redo(&mut self, line: &mut LineBuffer) -> bool {
        let mut waiting_for_end = 0;
        let mut redone = false;
        loop {
            if let Some(change) = self.redos.pop() {
                match change {
                    Change::Begin => {
                        waiting_for_end += 1;
                    }
                    Change::End => {
                        waiting_for_end -= 1;
                    }
                    _ => {
                        change.redo(line);
                        redone = true;
                    }
                };
                self.undos.push(change);
            } else {
                break;
            }
            if waiting_for_end <= 0 {
                break;
            }
        }
        redone
    }

    pub fn last_insert(&self) -> Option<String> {
        for change in self.undos.iter().rev() {
            match change {
                Change::Insert { ref text, .. } => return Some(text.to_owned()),
                Change::Replace { ref new, .. } => return Some(new.to_owned()),
                Change::End => {
                    continue;
                }
                _ => {
                    return None;
                }
            }
        }
        None
    }
}

impl DeleteListener for Changeset {
    fn start_killing(&mut self) {}

    fn delete(&mut self, idx: usize, string: &str, _: Direction) {
        self.delete(idx, string);
    }

    fn stop_killing(&mut self) {}
}
impl ChangeListener for Changeset {
    fn insert_char(&mut self, idx: usize, c: char) {
        self.insert(idx, c);
    }

    fn insert_str(&mut self, idx: usize, string: &str) {
        self.insert_str(idx, string);
    }

    fn replace(&mut self, idx: usize, old: &str, new: &str) {
        self.replace(idx, old, new);
    }
}

#[cfg(test)]
mod tests {
    use super::Changeset;
    use crate::line_buffer::LineBuffer;

    #[test]
    fn test_insert_chars() {
        let mut cs = Changeset::new();
        cs.insert(0, 'H');
        cs.insert(1, 'i');
        assert_eq!(1, cs.undos.len());
        assert_eq!(0, cs.redos.len());
        cs.insert(0, ' ');
        assert_eq!(2, cs.undos.len());
    }

    #[test]
    fn test_insert_strings() {
        let mut cs = Changeset::new();
        cs.insert_str(0, "Hello");
        cs.insert_str(5, ", ");
        assert_eq!(2, cs.undos.len());
        assert_eq!(0, cs.redos.len());
    }

    #[test]
    fn test_undo_insert() {
        let mut buf = LineBuffer::init("", 0, None);
        buf.insert_str(0, "Hello");
        buf.insert_str(5, ", world!");
        let mut cs = Changeset::new();
        assert_eq!(buf.as_str(), "Hello, world!");

        cs.insert_str(5, ", world!");

        cs.undo(&mut buf, 1);
        assert_eq!(0, cs.undos.len());
        assert_eq!(1, cs.redos.len());
        assert_eq!(buf.as_str(), "Hello");

        cs.redo(&mut buf);
        assert_eq!(1, cs.undos.len());
        assert_eq!(0, cs.redos.len());
        assert_eq!(buf.as_str(), "Hello, world!");
    }

    #[test]
    fn test_undo_delete() {
        let mut buf = LineBuffer::init("", 0, None);
        buf.insert_str(0, "Hello");
        let mut cs = Changeset::new();
        assert_eq!(buf.as_str(), "Hello");

        cs.delete(5, ", world!");

        cs.undo(&mut buf, 1);
        assert_eq!(buf.as_str(), "Hello, world!");

        cs.redo(&mut buf);
        assert_eq!(buf.as_str(), "Hello");
    }

    #[test]
    fn test_delete_chars() {
        let mut buf = LineBuffer::init("", 0, None);
        buf.insert_str(0, "Hlo");

        let mut cs = Changeset::new();
        cs.delete(1, "e");
        cs.delete(1, "l");
        assert_eq!(1, cs.undos.len());

        cs.undo(&mut buf, 1);
        assert_eq!(buf.as_str(), "Hello");
    }

    #[test]
    fn test_backspace_chars() {
        let mut buf = LineBuffer::init("", 0, None);
        buf.insert_str(0, "Hlo");

        let mut cs = Changeset::new();
        cs.delete(2, "l");
        cs.delete(1, "e");
        assert_eq!(1, cs.undos.len());

        cs.undo(&mut buf, 1);
        assert_eq!(buf.as_str(), "Hello");
    }

    #[test]
    fn test_undo_replace() {
        let mut buf = LineBuffer::init("", 0, None);
        buf.insert_str(0, "Hello, world!");
        let mut cs = Changeset::new();
        assert_eq!(buf.as_str(), "Hello, world!");

        buf.replace(1..5, "i");
        assert_eq!(buf.as_str(), "Hi, world!");
        cs.replace(1, "ello", "i");

        cs.undo(&mut buf, 1);
        assert_eq!(buf.as_str(), "Hello, world!");

        cs.redo(&mut buf);
        assert_eq!(buf.as_str(), "Hi, world!");
    }

    #[test]
    fn test_last_insert() {
        let mut cs = Changeset::new();
        cs.begin();
        cs.delete(0, "Hello");
        cs.insert_str(0, "Bye");
        cs.end();
        let insert = cs.last_insert();
        assert_eq!(Some("Bye".to_owned()), insert);
    }

    #[test]
    fn test_end() {
        let mut cs = Changeset::new();
        cs.begin();
        assert!(!cs.end());
        cs.begin();
        cs.insert_str(0, "Hi");
        assert!(cs.end());
    }
}
<|endoftext|>