<commit_before>use memory::Memory;
use std::vec::Vec;
use std::sync::atomic::{AtomicIsize, Ordering};

/// Maps multiple `Memory`s into a single address space.
///
/// The `MappedMemory` allows to unify multiple `Memory`s in one address space.
/// A `Memory` is mounted at a certain address and is from now on responsible
/// for all reads and writes starting at that address (the “mount point”) and
/// ending where the mounted memory ends. The read and write calls for the
/// responsible `Memory` will be given an address _relative_ to e mount point.
///
/// ```text
///                   Unmapped
///                <-------->
///       mem_a                   mem_b
/// |--------------|            |------|
///                            NullMemory
///                         |--------------
/// |------------- mapped_mem ----------->
/// |              |            |      |
/// 0            0x100        0x200  0x280
/// ```
///
/// For example: `mapped_mem.get(0x208)` would yield the same value as
/// `mem_b.get(0x008)`.
///
/// # Examples
///
/// ```
/// use osciemu::memory::{Memory, SliceMemory, MappedMemory};
///
/// let mut mm = MappedMemory::new();
/// let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1]))));
/// let m2 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([2, 2]))));
/// // Now mm =~ [1, _, 2, 2]
/// assert_eq!(mm.get(0), 1);
/// assert_eq!(mm.get(3), 2);
/// ```
///
/// A call to `mount` returns a `MemoryToken`. These tokens can be used to
/// borrow a `Memory` even if it has been mounted. This works similar to a
/// `std::cell::RefCell`.
///
/// ```
/// use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// use osciemu::memory::mappedmemory::MemoryToken;
///
/// let mut mm = MappedMemory::new();
/// let m1 = mm.mount(0, Box::new(NullMemory::new()));
/// let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4]))));
/// mm.set(0, 99);
/// assert_eq!(mm.get(0), 99);
/// assert_eq!(mm.borrow(&m2).get(0), 99);
/// ```
///
/// The `MemoryToken`s are also used to disable a mounted `Memory`. A disabled
/// memory is effectively unmounted, but remains in the ownership of the
/// `MappedMemory` and can be enabled using `enable_mount()`.
///
/// ```
/// # use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// # use osciemu::memory::mappedmemory::MemoryToken;
/// # let mut mm = MappedMemory::new();
/// # let m1 = mm.mount(0, Box::new(NullMemory::new()));
/// # let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4]))));
/// # mm.set(0, 99);
/// // ...
/// mm.disable_mount(&m2);
/// assert_eq!(mm.get(0), 0);
/// assert_eq!(mm.borrow(&m2).get(0), 99);
/// ```
///
/// To move a memory out of the `MappedMemory`, a memory can be unmounted:
///
/// ```
/// # use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// # use osciemu::memory::mappedmemory::MemoryToken;
/// # let mut mm = MappedMemory::new();
/// # let m1 = mm.mount(0, Box::new(NullMemory::new()));
/// # let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4]))));
/// # mm.set(0, 99);
/// // ...
/// let m2 = mm.unmount(m2);
/// assert_eq!(mm.get(0), 0);
/// assert_eq!(m2.get(0), 99);
/// ```
///
/// # Panics
/// `MappedMemory` panics when an unmapped address is read or written.
pub struct MappedMemory {
    memories: Vec<Entry>,
}

static ID_COUNTER: AtomicIsize = AtomicIsize::new(0);

struct Entry {
    id: isize,
    start_address: usize,
    size: usize,
    enabled: bool,
    memory: Box<Memory>,
}

impl Entry {
    fn contains(&self, addr: usize) -> bool {
        self.start_address <= addr && self.start_address + self.size > addr
    }
}

#[derive(Clone)]
pub struct MemoryToken {
    id: isize,
}

impl MappedMemory {
    pub fn new() -> MappedMemory {
        MappedMemory {
            memories: Vec::new(),
        }
    }

    /// Mounts a `Memory` at the given address.
    pub fn mount(&mut self, start_address: usize, memory: Box<Memory>) -> MemoryToken {
        let size = memory.size();
        let id = ID_COUNTER.fetch_add(1, Ordering::Relaxed);
        let new_entry = Entry {
            id,
            start_address,
            size,
            memory,
            enabled: true,
        };
        self.memories.push(new_entry);
        MemoryToken { id }
    }

    /// Unmounts a memory.
    pub fn unmount(&mut self, token: MemoryToken) -> Box<Memory> {
        let idx = self.memories
            .iter()
            .enumerate()
            .find(|&(_idx, ref entry)| entry.id == token.id)
            .map(|(idx, ref _entry)| idx)
            .unwrap();

        self.memories.remove(idx).memory
    }

    // Checks if a mounted memory is enabled.
    pub fn is_enabled_mount(&self, token: &MemoryToken) -> bool {
        self.entry_for_token(token).enabled
    }

    /// Disables a memory. This is the same as unmounting without moving
    /// ownership out of `MappedMemory`.
    pub fn disable_mount(&mut self, token: &MemoryToken) {
        let entry = self.entry_for_token_mut(token);
        entry.enabled = false;
    }

    // Enables a memory. The mount point remains unchanged.
    pub fn enable_mount(&mut self, token: &MemoryToken) {
        let entry = self.entry_for_token_mut(token);
        entry.enabled = true;
    }

    /// Borrows a memory.
    pub fn borrow(&self, token: &MemoryToken) -> &Box<Memory> {
        &self.entry_for_token(token).memory
    }

    /// Mutable borrows a memory.
    pub fn borrow_mut(&mut self, token: &MemoryToken) -> &mut Box<Memory> {
        &mut self.entry_for_token_mut(token).memory
    }

    fn entry_for_token(&self, token: &MemoryToken) -> &Entry {
        self.memories
            .iter()
            .find(|ref entry| entry.id == token.id)
            .unwrap()
    }

    fn entry_for_token_mut(&mut self, token: &MemoryToken) -> &mut Entry {
        self.memories
            .iter_mut()
            .find(|ref mut entry| entry.id == token.id)
            .unwrap()
    }

    fn enabled_entry_at_addr(&self, addr: usize) -> Option<&Entry> {
        self.memories
            .iter()
            .rev()
            .filter(|entry| entry.enabled)
            .find(|entry| entry.contains(addr))
    }

    fn enabled_entry_at_addr_mut(&mut self, addr: usize) -> Option<&mut Entry> {
        self.memories
            .iter_mut()
            .rev()
            .filter(|entry| entry.enabled)
            .find(|entry| entry.contains(addr))
    }
}

impl Memory for MappedMemory {
    fn get(&self, addr: usize) -> i32 {
        self.enabled_entry_at_addr(addr)
            .map(|entry| entry.memory.get(addr - entry.start_address))
            .expect("Out of bounds")
    }

    fn set(&mut self, addr: usize, value: i32) {
        self.enabled_entry_at_addr_mut(addr)
            .map(|entry| entry.memory.set(addr - entry.start_address, value))
            .expect("Out of bounds")
    }

    fn size(&self) -> usize {
        self.memories
            .iter()
            .filter(|entry| entry.enabled)
            .map(|entry| entry.start_address + entry.size)
            .max()
            .unwrap_or(0)
    }
}

#[cfg(test)]
mod tests {
    use memory::{Memory, NullMemory, SliceMemory};

    #[test]
    #[allow(unused_variables)]
    fn overlapping_mounts() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(NullMemory::new()));
        let m2 = mm.mount(1, Box::new(SliceMemory::from_slice(Box::new([2, 2]))));
        let m3 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([3]))));
        assert_eq!(mm.get(0), 0);
        assert_eq!(mm.get(1), 2);
        assert_eq!(mm.get(2), 3);
        assert_eq!(mm.get(3), 0);
    }

    #[test]
    #[allow(unused_variables)]
    fn get_and_set() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1]))));
        let m2 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([2, 2]))));

        assert_eq!(mm.get(0), 1);
        assert_eq!(mm.get(2), 2);

        mm.set(0, 3);
        assert_eq!(mm.get(0), 3);

        mm.set(3, 0);
        assert_eq!(mm.get(3), 0);
        assert_eq!(mm.borrow(&m1).get(0), 3);
        assert_eq!(mm.borrow(&m2).get(1), 0);
    }

    #[test]
    #[allow(unused_variables)]
    fn size() {
        let mut mm = super::MappedMemory::new();
        let m1 = Box::new(SliceMemory::from_slice(Box::new([1])));
        let m2 = Box::new(SliceMemory::from_slice(Box::new([2, 2])));
        assert_eq!(mm.size(), 0);

        mm.mount(0, m1);
        assert_eq!(mm.size(), 1);

        mm.mount(2, m2);
        assert_eq!(mm.size(), 4);
    }

    #[test]
    #[allow(unused_variables)]
    fn size_with_overlap() {
        let mut mm = super::MappedMemory::new();
        let m1 = Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1])));
        let m2 = Box::new(SliceMemory::from_slice(Box::new([2, 2])));
        let m3 = Box::new(SliceMemory::from_slice(Box::new([3, 3, 3])));

        mm.mount(0, m1);
        mm.mount(2, m2);
        assert_eq!(mm.size(), 5);

        mm.mount(6, m3);
        assert_eq!(mm.size(), 9);
    }

    #[test]
    #[allow(unused_variables)]
    fn disable_mount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(
            0,
            Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))),
        );
        let m2 = mm.mount(
            0,
            Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))),
        );
        let m3 = mm.mount(
            0,
            Box::new(SliceMemory::from_slice(Box::new([3, 3, 3, 3, 3]))),
        );

        for i in 0..5 {
            assert_eq!(mm.get(i), 3);
        }
        mm.disable_mount(&m3);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        mm.disable_mount(&m1);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
    }

    #[test]
    #[allow(unused_variables)]
    fn enable_mount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(
            0,
            Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))),
        );
        let m2 = mm.mount(
            0,
            Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))),
        );

        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        mm.disable_mount(&m2);
        for i in 0..5 {
            assert_eq!(mm.get(i), 1);
        }
        mm.enable_mount(&m2);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
    }

    #[test]
    #[allow(unused_variables)]
    fn unmount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(
            0,
            Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))),
        );
        let m2 = mm.mount(
            0,
            Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))),
        );

        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        let m2 = mm.unmount(m2);
        for i in 0..5 {
            assert_eq!(mm.get(i), 1);
            assert_eq!(m2.get(i), 2);
        }
    }

    #[test]
    #[allow(unused_variables)]
    fn is_enabled_mount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(
            0,
            Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))),
        );
        let m2 = mm.mount(
            0,
            Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))),
        );

        assert!(mm.is_enabled_mount(&m2));
        mm.disable_mount(&m2);
        assert!(!mm.is_enabled_mount(&m2));
        mm.enable_mount(&m2);
        assert!(mm.is_enabled_mount(&m2));
    }

    #[test]
    #[allow(unused_variables)]
    #[should_panic]
    fn read_disabled() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1]))));
        let m2 = mm.mount(1, Box::new(SliceMemory::from_slice(Box::new([2]))));
        let m3 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([3]))));
        mm.disable_mount(&m2);
        mm.get(1);
    }
}
<commit_msg>docs(mappedmemory): Update documentation<commit_after>//! Maps multiple `Memory`s into a single address space.
use memory::Memory;
use std::vec::Vec;
use std::sync::atomic::{AtomicIsize, Ordering};

/// The `MappedMemory` allows to unify multiple `Memory`s in one address space.
///
/// A `Memory` is mounted at a certain address and is from now on responsible for all reads and writes starting at that address (the “mount point”) and ending where the mounted memory ends. The read and write calls for the responsible `Memory` will be given an address _relative_ to the mount point.
///
/// More recent mounts will shadow earlier mounts.
///
/// ```text
///                   Unmapped
///                <-------->
///       mem_a                   mem_b
/// |--------------|            |------|
///                            NullMemory
///                         |--------------
/// |------------- mapped_mem ----------->
/// |              |            |      |
/// 0            0x100        0x200  0x280
/// ```
///
/// For example: `mapped_mem.get(0x208)` would yield the same value as `mem_b.get(0x008)`.
///
/// # Examples
///
/// ```
/// use osciemu::memory::{Memory, SliceMemory, MappedMemory};
///
/// let mut mm = MappedMemory::new();
/// let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1]))));
/// let m2 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([2, 2]))));
/// // Now mm =~ [1, _, 2, 2]
/// assert_eq!(mm.get(0), 1);
/// assert_eq!(mm.get(3), 2);
/// ```
///
/// A call to `mount` returns a `MemoryToken`. These tokens can be used to
/// borrow a `Memory` even if it has been mounted. This works similar to a
/// `std::cell::RefCell`.
///
/// ```
/// # use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// # use osciemu::memory::mappedmemory::MemoryToken;
///
/// let mut mm = MappedMemory::new();
/// let m1 = mm.mount(0, Box::new(NullMemory::new()));
/// let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4]))));
/// mm.set(0, 99);
/// assert_eq!(mm.get(0), 99);
/// assert_eq!(mm.borrow(&m2).get(0), 99);
/// ```
///
/// The `MemoryToken`s are also used to disable a mounted `Memory`. A disabled
/// memory is effectively unmounted, but remains in the ownership of the
/// `MappedMemory` and can be re-enabled using `enable_mount()`.
///
/// ```
/// # use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// # use osciemu::memory::mappedmemory::MemoryToken;
/// # let mut mm = MappedMemory::new();
/// # let m1 = mm.mount(0, Box::new(NullMemory::new()));
/// # let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4]))));
/// # mm.set(0, 99);
/// // ...
/// mm.disable_mount(&m2);
/// assert_eq!(mm.get(0), 0);
/// assert_eq!(mm.borrow(&m2).get(0), 99);
/// ```
///
/// To move a memory out of `MappedMemory`’s ownership, a memory can be unmounted. Note that the returned type will be `Box<Memory>` and the underlying type is lost.
///
/// ```
/// # use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// # use osciemu::memory::mappedmemory::MemoryToken;
/// # let mut mm = MappedMemory::new();
/// # let m1 = mm.mount(0, Box::new(NullMemory::new()));
/// # let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4]))));
/// # mm.set(0, 99);
/// // ...
/// let m2 = mm.unmount(m2);
/// assert_eq!(mm.get(0), 0);
/// assert_eq!(m2.get(0), 99);
/// ```
///
/// # Panics
/// `MappedMemory` panics when an unmapped address is read or written.
pub struct MappedMemory {
    memories: Vec<Entry>,
}

static ID_COUNTER: AtomicIsize = AtomicIsize::new(0);

struct Entry {
    id: isize,
    start_address: usize,
    size: usize,
    enabled: bool,
    memory: Box<Memory>,
}

impl Entry {
    fn contains(&self, addr: usize) -> bool {
        self.start_address <= addr && self.start_address + self.size > addr
    }
}

#[derive(Clone)]
/// Represents a `Memory` in the ownership of `MappedMemory`.
pub struct MemoryToken {
    id: isize,
}

impl MappedMemory {
    // Create a new `MappedMemory`.
    pub fn new() -> MappedMemory {
        MappedMemory {
            memories: Vec::new(),
        }
    }

    /// Mounts a `Memory` at the given address.
    ///
    /// More recent mounts will take precedence over earlier mounts, effectively “shadowing” the earlier mounts.
    pub fn mount(&mut self, start_address: usize, memory: Box<Memory>) -> MemoryToken {
        let size = memory.size();
        let id = ID_COUNTER.fetch_add(1, Ordering::Relaxed);
        let new_entry = Entry {
            id,
            start_address,
            size,
            memory,
            enabled: true,
        };
        self.memories.push(new_entry);
        MemoryToken { id }
    }

    /// Unmounts a memory.
    pub fn unmount(&mut self, token: MemoryToken) -> Box<Memory> {
        let idx = self.memories
            .iter()
            .enumerate()
            .find(|&(_idx, ref entry)| entry.id == token.id)
            .map(|(idx, ref _entry)| idx)
            .unwrap();

        self.memories.remove(idx).memory
    }

    // Checks if a memory is enabled.
    pub fn is_enabled_mount(&self, token: &MemoryToken) -> bool {
        self.entry_for_token(token).enabled
    }

    /// Disables a memory. This is the same as unmounting without moving
    /// ownership out of `MappedMemory`.
    pub fn disable_mount(&mut self, token: &MemoryToken) {
        let entry = self.entry_for_token_mut(token);
        entry.enabled = false;
    }

    // Enables a memory. The mount point remains unchanged.
    pub fn enable_mount(&mut self, token: &MemoryToken) {
        let entry = self.entry_for_token_mut(token);
        entry.enabled = true;
    }

    /// Borrows a memory.
    pub fn borrow(&self, token: &MemoryToken) -> &Box<Memory> {
        &self.entry_for_token(token).memory
    }

    /// Mutably borrows a memory.
    pub fn borrow_mut(&mut self, token: &MemoryToken) -> &mut Box<Memory> {
        &mut self.entry_for_token_mut(token).memory
    }

    fn entry_for_token(&self, token: &MemoryToken) -> &Entry {
        self.memories
            .iter()
            .find(|ref entry| entry.id == token.id)
            .unwrap()
    }

    fn entry_for_token_mut(&mut self, token: &MemoryToken) -> &mut Entry {
        self.memories
            .iter_mut()
            .find(|ref mut entry| entry.id == token.id)
            .unwrap()
    }

    fn enabled_entry_at_addr(&self, addr: usize) -> Option<&Entry> {
        self.memories
            .iter()
            .rev()
            .filter(|entry| entry.enabled)
            .find(|entry| entry.contains(addr))
    }

    fn enabled_entry_at_addr_mut(&mut self, addr: usize) -> Option<&mut Entry> {
        self.memories
            .iter_mut()
            .rev()
            .filter(|entry| entry.enabled)
            .find(|entry| entry.contains(addr))
    }
}

impl Memory for MappedMemory {
    fn get(&self, addr: usize) -> i32 {
        self.enabled_entry_at_addr(addr)
            .map(|entry| entry.memory.get(addr - entry.start_address))
            .expect("Out of bounds")
    }

    fn set(&mut self, addr: usize, value: i32) {
        self.enabled_entry_at_addr_mut(addr)
            .map(|entry| entry.memory.set(addr - entry.start_address, value))
            .expect("Out of bounds")
    }

    fn size(&self) -> usize {
        self.memories
            .iter()
            .filter(|entry| entry.enabled)
            .map(|entry| entry.start_address + entry.size)
            .max()
            .unwrap_or(0)
    }
}

#[cfg(test)]
mod tests {
    use memory::{Memory, NullMemory, SliceMemory};

    #[test]
    #[allow(unused_variables)]
    fn overlapping_mounts() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(NullMemory::new()));
        let m2 = mm.mount(1, Box::new(SliceMemory::from_slice(Box::new([2, 2]))));
        let m3 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([3]))));
        assert_eq!(mm.get(0), 0);
        assert_eq!(mm.get(1), 2);
        assert_eq!(mm.get(2), 3);
        assert_eq!(mm.get(3), 0);
    }

    #[test]
    #[allow(unused_variables)]
    fn get_and_set() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1]))));
        let m2 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([2, 2]))));

        assert_eq!(mm.get(0), 1);
        assert_eq!(mm.get(2), 2);

        mm.set(0, 3);
        assert_eq!(mm.get(0), 3);

        mm.set(3, 0);
        assert_eq!(mm.get(3), 0);
        assert_eq!(mm.borrow(&m1).get(0), 3);
        assert_eq!(mm.borrow(&m2).get(1), 0);
    }

    #[test]
    #[allow(unused_variables)]
    fn size() {
        let mut mm = super::MappedMemory::new();
        let m1 = Box::new(SliceMemory::from_slice(Box::new([1])));
        let m2 = Box::new(SliceMemory::from_slice(Box::new([2, 2])));
        assert_eq!(mm.size(), 0);

        mm.mount(0, m1);
        assert_eq!(mm.size(), 1);

        mm.mount(2, m2);
        assert_eq!(mm.size(), 4);
    }

    #[test]
    #[allow(unused_variables)]
    fn size_with_overlap() {
        let mut mm = super::MappedMemory::new();
        let m1 = Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1])));
        let m2 = Box::new(SliceMemory::from_slice(Box::new([2, 2])));
        let m3 = Box::new(SliceMemory::from_slice(Box::new([3, 3, 3])));

        mm.mount(0, m1);
        mm.mount(2, m2);
        assert_eq!(mm.size(), 5);

        mm.mount(6, m3);
        assert_eq!(mm.size(), 9);
    }

    #[test]
    #[allow(unused_variables)]
    fn disable_mount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(
            0,
            Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))),
        );
        let m2 = mm.mount(
            0,
            Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))),
        );
        let m3 = mm.mount(
            0,
            Box::new(SliceMemory::from_slice(Box::new([3, 3, 3, 3, 3]))),
        );

        for i in 0..5 {
            assert_eq!(mm.get(i), 3);
        }
        mm.disable_mount(&m3);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        mm.disable_mount(&m1);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
    }

    #[test]
    #[allow(unused_variables)]
    fn enable_mount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(
            0,
            Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))),
        );
        let m2 = mm.mount(
            0,
            Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))),
        );

        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        mm.disable_mount(&m2);
        for i in 0..5 {
            assert_eq!(mm.get(i), 1);
        }
        mm.enable_mount(&m2);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
    }

    #[test]
    #[allow(unused_variables)]
    fn unmount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(
            0,
            Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))),
        );
        let m2 = mm.mount(
            0,
            Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))),
        );

        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        let m2 = mm.unmount(m2);
        for i in 0..5 {
            assert_eq!(mm.get(i), 1);
            assert_eq!(m2.get(i), 2);
        }
    }

    #[test]
    #[allow(unused_variables)]
    fn is_enabled_mount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(
            0,
            Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))),
        );
        let m2 = mm.mount(
            0,
            Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))),
        );

        assert!(mm.is_enabled_mount(&m2));
        mm.disable_mount(&m2);
        assert!(!mm.is_enabled_mount(&m2));
        mm.enable_mount(&m2);
        assert!(mm.is_enabled_mount(&m2));
    }

    #[test]
    #[allow(unused_variables)]
    #[should_panic]
    fn read_disabled() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1]))));
        let m2 = mm.mount(1, Box::new(SliceMemory::from_slice(Box::new([2]))));
        let m3 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([3]))));
        mm.disable_mount(&m2);
        mm.get(1);
    }
}
<|endoftext|>


<commit_before>//! Tree nodes, the representation of SVG elements.
//!
//! Librsvg uses the [rctree crate][rctree] to represent the SVG tree of elements.
//! Its [`Node`] struct provides a generic wrapper over nodes in a tree.
//! Librsvg puts a [`NodeData`] as the type parameter of [`Node`].  For convenience,
//! librsvg has a type alias `Node = Node<NodeData>`.
//!
//! Nodes are not constructed directly by callers;
//!
//! [rctree]: ../../rctree/index.html
//! [`Node`]: ../../rctree/struct.Node.html
//! [`NodeData`]: struct.NodeData.html

use markup5ever::QualName;
use std::cell::{Ref, RefMut};
use std::fmt;

use crate::bbox::BoundingBox;
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::*;
use crate::error::*;
use crate::properties::ComputedValues;
use crate::text::Chars;
use crate::xml::Attributes;

/// Strong reference to an element in the SVG tree.
///
/// See the [module documentation](index.html) for more information.
pub type Node = rctree::Node<NodeData>;

/// Weak reference to an element in the SVG tree.
///
/// See the [module documentation](index.html) for more information.
pub type WeakNode = rctree::WeakNode<NodeData>;

/// Data for a single DOM node.
///
/// ## Memory consumption
///
/// SVG files look like this, roughly:
///
/// ```xml
/// <svg>
///   <rect x="10" y="20"/>
///   <path d="..."/>
///   <text x="10" y="20">Hello</text>
///   <!-- etc -->
/// </svg>
/// ```
///
/// Each element has a bunch of data, including the styles, which is
/// the biggest consumer of memory within the `Element` struct.  But
/// between each element there is a text node; in the example above
/// there are a bunch of text nodes with just whitespace (newlines and
/// spaces), and a single text node with "`Hello`" in it from the
/// `<text>` element.
///
/// ## Accessing the node's contents
///
/// Code that traverses the DOM tree needs to find out at runtime what
/// each node stands for.  First, use the `is_chars` or `is_element`
/// methods from the `NodeBorrow` trait to see if you can then call
/// `borrow_chars`, `borrow_element`, or `borrow_element_mut`.
pub enum NodeData {
    Element(Element),
    Text(Chars),
}

impl NodeData {
    pub fn new_element(name: &QualName, attrs: Attributes) -> NodeData {
        NodeData::Element(Element::new(name, attrs))
    }

    pub fn new_chars(initial_text: &str) -> NodeData {
        NodeData::Text(Chars::new(initial_text))
    }
}

impl fmt::Display for NodeData {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            NodeData::Element(ref e) => {
                write!(f, "{}", e)?;
            }
            NodeData::Text(_) => {
                write!(f, "Chars")?;
            }
        }

        Ok(())
    }
}

/// Can obtain computed values from a node
///
/// In our tree of SVG elements (Node in our parlance), each node stores a `ComputedValues` that
/// gets computed during the initial CSS cascade.  However, sometimes nodes need to be rendered
/// outside the normal hierarchy.  For example, the `<use>` element can "instance" a subtree from
/// elsewhere in the SVG; it causes the instanced subtree to re-cascade from the computed values for
/// the `<use>` element.
///
/// You can then call the `get()` method on the resulting `CascadedValues` to get a
/// `&ComputedValues` whose fields you can access.
pub struct CascadedValues<'a> {
    inner: CascadedInner<'a>,
}

enum CascadedInner<'a> {
    FromNode(Ref<'a, Element>),
    FromValues(Box<ComputedValues>),
}

impl<'a> CascadedValues<'a> {
    /// Creates a `CascadedValues` that has the same cascading mode as &self
    ///
    /// This is what nodes should normally use to draw their children from their `draw()` method.
    /// Nodes that need to override the cascade for their children can use `new_from_values()`
    /// instead.
    pub fn new(&self, node: &'a Node) -> CascadedValues<'a> {
        match self.inner {
            CascadedInner::FromNode(_) => CascadedValues {
                inner: CascadedInner::FromNode(node.borrow_element()),
            },

            CascadedInner::FromValues(ref v) => CascadedValues::new_from_values(node, &*v),
        }
    }

    /// Creates a `CascadedValues` that will hold the `node`'s computed values
    ///
    /// This is to be used only in the toplevel drawing function, or in elements like `<marker>`
    /// that don't propagate their parent's cascade to their children.  All others should use
    /// `new()` to derive the cascade from an existing one.
    pub fn new_from_node(node: &Node) -> CascadedValues<'_> {
        CascadedValues {
            inner: CascadedInner::FromNode(node.borrow_element()),
        }
    }

    /// Creates a `CascadedValues` that will override the `node`'s cascade with the specified
    /// `values`
    ///
    /// This is for the `<use>` element, which draws the element which it references with the
    /// `<use>`'s own cascade, not with the element's original cascade.
    pub fn new_from_values(node: &'a Node, values: &ComputedValues) -> CascadedValues<'a> {
        let mut v = Box::new(values.clone());
        node.borrow_element()
            .get_specified_values()
            .to_computed_values(&mut v);

        CascadedValues {
            inner: CascadedInner::FromValues(v),
        }
    }

    /// Returns the cascaded `ComputedValues`.
    ///
    /// Nodes should use this from their `Draw::draw()` implementation to get the
    /// `ComputedValues` from the `CascadedValues` that got passed to `draw()`.
    pub fn get(&'a self) -> &'a ComputedValues {
        match self.inner {
            CascadedInner::FromNode(ref e) => e.get_computed_values(),
            CascadedInner::FromValues(ref v) => &*v,
        }
    }
}

/// Helper trait to get different NodeData variants
pub trait NodeBorrow {
    /// Returns `false` for NodeData::Text, `true` otherwise.
    fn is_element(&self) -> bool;

    /// Returns `true` for NodeData::Text, `false` otherwise.
    fn is_chars(&self) -> bool;

    /// Borrows a `Chars` reference.
    ///
    /// Panics: will panic if `&self` is not a `NodeData::Text` node
    fn borrow_chars(&self) -> Ref<'_, Chars>;

    /// Borrows an `Element` reference
    ///
    /// Panics: will panic if `&self` is not a `NodeData::Element` node
    fn borrow_element(&self) -> Ref<'_, Element>;

    /// Borrows an `Element` reference mutably
    ///
    /// Panics: will panic if `&self` is not a `NodeData::Element` node
    fn borrow_element_mut(&mut self) -> RefMut<'_, Element>;
}

impl NodeBorrow for Node {
    fn is_element(&self) -> bool {
        matches!(*self.borrow(), NodeData::Element(_))
    }

    fn is_chars(&self) -> bool {
        matches!(*self.borrow(), NodeData::Text(_))
    }

    fn borrow_chars(&self) -> Ref<'_, Chars> {
        Ref::map(self.borrow(), |n| match *n {
            NodeData::Text(ref c) => c,
            _ => panic!("tried to borrow_chars for a non-text node"),
        })
    }

    fn borrow_element(&self) -> Ref<'_, Element> {
        Ref::map(self.borrow(), |n| match *n {
            NodeData::Element(ref e) => e,
            _ => panic!("tried to borrow_element for a non-element node"),
        })
    }

    fn borrow_element_mut(&mut self) -> RefMut<'_, Element> {
        RefMut::map(self.borrow_mut(), |n| match *n {
            NodeData::Element(ref mut e) => e,
            _ => panic!("tried to borrow_element_mut for a non-element node"),
        })
    }
}

#[macro_export]
macro_rules! is_element_of_type {
    ($node:expr, $element_type:ident) => {
        matches!(
            *$node.borrow_element(),
            crate::element::Element::$element_type(_)
        )
    };
}

#[macro_export]
macro_rules! borrow_element_as {
    ($node:expr, $element_type:ident) => {
        std::cell::Ref::map($node.borrow_element(), |e| match *e {
            crate::element::Element::$element_type(ref e) => &*e,
            _ => panic!("tried to borrow_element_as {}", stringify!($element_type)),
        })
    };
}

/// Helper trait for cascading recursively
pub trait NodeCascade {
    fn cascade(&mut self, values: &ComputedValues);
}

impl NodeCascade for Node {
    fn cascade(&mut self, values: &ComputedValues) {
        let mut values = values.clone();

        {
            let mut elt = self.borrow_element_mut();

            elt.get_specified_values().to_computed_values(&mut values);
            elt.set_computed_values(&values);
        }

        for mut child in self.children().filter(|c| c.is_element()) {
            child.cascade(&values);
        }
    }
}

/// Helper trait for drawing recursively
pub trait NodeDraw {
    fn draw(
        &self,
        acquired_nodes: &mut AcquiredNodes<'_>,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<BoundingBox, RenderingError>;

    fn draw_children(
        &self,
        acquired_nodes: &mut AcquiredNodes<'_>,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<BoundingBox, RenderingError>;
}

impl NodeDraw for Node {
    fn draw(
        &self,
        acquired_nodes: &mut AcquiredNodes<'_>,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<BoundingBox, RenderingError> {
        match *self.borrow() {
            NodeData::Element(ref e) => e.draw(self, acquired_nodes, cascaded, draw_ctx, clipping),
            _ => Ok(draw_ctx.empty_bbox()),
        }
    }

    fn draw_children(
        &self,
        acquired_nodes: &mut AcquiredNodes<'_>,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<BoundingBox, RenderingError> {
        let mut bbox = draw_ctx.empty_bbox();

        for child in self.children().filter(|c| c.is_element()) {
            let child_bbox = draw_ctx.draw_node_from_stack(
                &child,
                acquired_nodes,
                &CascadedValues::new(cascaded, &child),
                clipping,
            )?;
            bbox.insert(&child_bbox);
        }

        Ok(bbox)
    }
}
<commit_msg>node.rs: links to doc items by name<commit_after>//! Tree nodes, the representation of SVG elements.
//!
//! Librsvg uses the [rctree crate][rctree] to represent the SVG tree of elements.
//! Its [`rctree::Node`] struct provides a generic wrapper over nodes in a tree.
//! Librsvg puts a [`NodeData`] as the type parameter of [`rctree::Node`].  For convenience,
//! librsvg has a type alias [`Node`]` = rctree::Node<NodeData>`.
//!
//! Nodes are not constructed directly by callers;

use markup5ever::QualName;
use std::cell::{Ref, RefMut};
use std::fmt;

use crate::bbox::BoundingBox;
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::*;
use crate::error::*;
use crate::properties::ComputedValues;
use crate::text::Chars;
use crate::xml::Attributes;

/// Strong reference to an element in the SVG tree.
///
/// See the [module documentation][self] for more information.
pub type Node = rctree::Node<NodeData>;

/// Weak reference to an element in the SVG tree.
///
/// See the [module documentation][self] for more information.
pub type WeakNode = rctree::WeakNode<NodeData>;

/// Data for a single DOM node.
///
/// ## Memory consumption
///
/// SVG files look like this, roughly:
///
/// ```xml
/// <svg>
///   <rect x="10" y="20"/>
///   <path d="..."/>
///   <text x="10" y="20">Hello</text>
///   <!-- etc -->
/// </svg>
/// ```
///
/// Each element has a bunch of data, including the styles, which is
/// the biggest consumer of memory within the `Element` struct.  But
/// between each element there is a text node; in the example above
/// there are a bunch of text nodes with just whitespace (newlines and
/// spaces), and a single text node with "`Hello`" in it from the
/// `<text>` element.
///
/// ## Accessing the node's contents
///
/// Code that traverses the DOM tree needs to find out at runtime what
/// each node stands for.  First, use the `is_chars` or `is_element`
/// methods from the `NodeBorrow` trait to see if you can then call
/// `borrow_chars`, `borrow_element`, or `borrow_element_mut`.
pub enum NodeData {
    Element(Element),
    Text(Chars),
}

impl NodeData {
    pub fn new_element(name: &QualName, attrs: Attributes) -> NodeData {
        NodeData::Element(Element::new(name, attrs))
    }

    pub fn new_chars(initial_text: &str) -> NodeData {
        NodeData::Text(Chars::new(initial_text))
    }
}

impl fmt::Display for NodeData {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            NodeData::Element(ref e) => {
                write!(f, "{}", e)?;
            }
            NodeData::Text(_) => {
                write!(f, "Chars")?;
            }
        }

        Ok(())
    }
}

/// Can obtain computed values from a node
///
/// In our tree of SVG elements (Node in our parlance), each node stores a `ComputedValues` that
/// gets computed during the initial CSS cascade.  However, sometimes nodes need to be rendered
/// outside the normal hierarchy.  For example, the `<use>` element can "instance" a subtree from
/// elsewhere in the SVG; it causes the instanced subtree to re-cascade from the computed values for
/// the `<use>` element.
///
/// You can then call the `get()` method on the resulting `CascadedValues` to get a
/// `&ComputedValues` whose fields you can access.
pub struct CascadedValues<'a> {
    inner: CascadedInner<'a>,
}

enum CascadedInner<'a> {
    FromNode(Ref<'a, Element>),
    FromValues(Box<ComputedValues>),
}

impl<'a> CascadedValues<'a> {
    /// Creates a `CascadedValues` that has the same cascading mode as &self
    ///
    /// This is what nodes should normally use to draw their children from their `draw()` method.
    /// Nodes that need to override the cascade for their children can use `new_from_values()`
    /// instead.
    pub fn new(&self, node: &'a Node) -> CascadedValues<'a> {
        match self.inner {
            CascadedInner::FromNode(_) => CascadedValues {
                inner: CascadedInner::FromNode(node.borrow_element()),
            },

            CascadedInner::FromValues(ref v) => CascadedValues::new_from_values(node, &*v),
        }
    }

    /// Creates a `CascadedValues` that will hold the `node`'s computed values
    ///
    /// This is to be used only in the toplevel drawing function, or in elements like `<marker>`
    /// that don't propagate their parent's cascade to their children.  All others should use
    /// `new()` to derive the cascade from an existing one.
    pub fn new_from_node(node: &Node) -> CascadedValues<'_> {
        CascadedValues {
            inner: CascadedInner::FromNode(node.borrow_element()),
        }
    }

    /// Creates a `CascadedValues` that will override the `node`'s cascade with the specified
    /// `values`
    ///
    /// This is for the `<use>` element, which draws the element which it references with the
    /// `<use>`'s own cascade, not with the element's original cascade.
    pub fn new_from_values(node: &'a Node, values: &ComputedValues) -> CascadedValues<'a> {
        let mut v = Box::new(values.clone());
        node.borrow_element()
            .get_specified_values()
            .to_computed_values(&mut v);

        CascadedValues {
            inner: CascadedInner::FromValues(v),
        }
    }

    /// Returns the cascaded `ComputedValues`.
    ///
    /// Nodes should use this from their `Draw::draw()` implementation to get the
    /// `ComputedValues` from the `CascadedValues` that got passed to `draw()`.
    pub fn get(&'a self) -> &'a ComputedValues {
        match self.inner {
            CascadedInner::FromNode(ref e) => e.get_computed_values(),
            CascadedInner::FromValues(ref v) => &*v,
        }
    }
}

/// Helper trait to get different NodeData variants
pub trait NodeBorrow {
    /// Returns `false` for NodeData::Text, `true` otherwise.
    fn is_element(&self) -> bool;

    /// Returns `true` for NodeData::Text, `false` otherwise.
    fn is_chars(&self) -> bool;

    /// Borrows a `Chars` reference.
    ///
    /// Panics: will panic if `&self` is not a `NodeData::Text` node
    fn borrow_chars(&self) -> Ref<'_, Chars>;

    /// Borrows an `Element` reference
    ///
    /// Panics: will panic if `&self` is not a `NodeData::Element` node
    fn borrow_element(&self) -> Ref<'_, Element>;

    /// Borrows an `Element` reference mutably
    ///
    /// Panics: will panic if `&self` is not a `NodeData::Element` node
    fn borrow_element_mut(&mut self) -> RefMut<'_, Element>;
}

impl NodeBorrow for Node {
    fn is_element(&self) -> bool {
        matches!(*self.borrow(), NodeData::Element(_))
    }

    fn is_chars(&self) -> bool {
        matches!(*self.borrow(), NodeData::Text(_))
    }

    fn borrow_chars(&self) -> Ref<'_, Chars> {
        Ref::map(self.borrow(), |n| match *n {
            NodeData::Text(ref c) => c,
            _ => panic!("tried to borrow_chars for a non-text node"),
        })
    }

    fn borrow_element(&self) -> Ref<'_, Element> {
        Ref::map(self.borrow(), |n| match *n {
            NodeData::Element(ref e) => e,
            _ => panic!("tried to borrow_element for a non-element node"),
        })
    }

    fn borrow_element_mut(&mut self) -> RefMut<'_, Element> {
        RefMut::map(self.borrow_mut(), |n| match *n {
            NodeData::Element(ref mut e) => e,
            _ => panic!("tried to borrow_element_mut for a non-element node"),
        })
    }
}

#[macro_export]
macro_rules! is_element_of_type {
    ($node:expr, $element_type:ident) => {
        matches!(
            *$node.borrow_element(),
            crate::element::Element::$element_type(_)
        )
    };
}

#[macro_export]
macro_rules! borrow_element_as {
    ($node:expr, $element_type:ident) => {
        std::cell::Ref::map($node.borrow_element(), |e| match *e {
            crate::element::Element::$element_type(ref e) => &*e,
            _ => panic!("tried to borrow_element_as {}", stringify!($element_type)),
        })
    };
}

/// Helper trait for cascading recursively
pub trait NodeCascade {
    fn cascade(&mut self, values: &ComputedValues);
}

impl NodeCascade for Node {
    fn cascade(&mut self, values: &ComputedValues) {
        let mut values = values.clone();

        {
            let mut elt = self.borrow_element_mut();

            elt.get_specified_values().to_computed_values(&mut values);
            elt.set_computed_values(&values);
        }

        for mut child in self.children().filter(|c| c.is_element()) {
            child.cascade(&values);
        }
    }
}

/// Helper trait for drawing recursively
pub trait NodeDraw {
    fn draw(
        &self,
        acquired_nodes: &mut AcquiredNodes<'_>,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<BoundingBox, RenderingError>;

    fn draw_children(
        &self,
        acquired_nodes: &mut AcquiredNodes<'_>,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<BoundingBox, RenderingError>;
}

impl NodeDraw for Node {
    fn draw(
        &self,
        acquired_nodes: &mut AcquiredNodes<'_>,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<BoundingBox, RenderingError> {
        match *self.borrow() {
            NodeData::Element(ref e) => e.draw(self, acquired_nodes, cascaded, draw_ctx, clipping),
            _ => Ok(draw_ctx.empty_bbox()),
        }
    }

    fn draw_children(
        &self,
        acquired_nodes: &mut AcquiredNodes<'_>,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<BoundingBox, RenderingError> {
        let mut bbox = draw_ctx.empty_bbox();

        for child in self.children().filter(|c| c.is_element()) {
            let child_bbox = draw_ctx.draw_node_from_stack(
                &child,
                acquired_nodes,
                &CascadedValues::new(cascaded, &child),
                clipping,
            )?;
            bbox.insert(&child_bbox);
        }

        Ok(bbox)
    }
}
<|endoftext|>


<commit_before>use core::cell::UnsafeCell;
use core::sync::atomic::{AtomicUsize, Ordering};

use util::cpu_relax;

/// A synchronization primitive which can be used to run a one-time global
/// initialization. Unlike its std equivalent, this is generalized so that The
/// closure returns a value and it is stored. Once therefore acts something like
/// 1a future, too.
///
/// # Examples
///
/// ```
/// #![feature(const_fn)]
/// use spin;
///
/// static START: spin::Once<()> = spin::Once::new();
///
/// START.call_once(|| {
///     // run initialization here
/// });
/// ```
pub struct Once<T> {
    state: AtomicUsize,
    data: UnsafeCell<Option<T>>, // TODO remove option and use mem::uninitialized
}

// Same unsafe impls as `std::sync::RwLock`, because this also allows for
// concurrent reads.
unsafe impl<T: Send + Sync> Sync for Once<T> {}
unsafe impl<T: Send> Send for Once<T> {}

// Four states that a Once can be in, encoded into the lower bits of `state` in
// the Once structure.
const INCOMPLETE: usize = 0x0;
const RUNNING: usize = 0x1;
const COMPLETE: usize = 0x2;
const PANICKED: usize = 0x3;

#[cfg(feature = "core_intrinsics")]
#[inline(always)]
fn unreachable() -> ! {
    unsafe { ::core::intrinsics::unreachable() }
}

#[cfg(not(feature = "core_intrinsics"))]
#[inline(always)]
fn unreachable() -> ! {
    unreachable!()
}

impl<T> Once<T> {
    /// Creates a new `Once` value.
    pub const fn new() -> Once<T> {
        Once {
            state: AtomicUsize::new(INCOMPLETE),
            data: UnsafeCell::new(None),
        }
    }

    fn force_get<'a>(&'a self) -> &'a T {
        match unsafe { &*self.data.get() }.as_ref() {
            None    => unreachable(),
            Some(p) => p,
        }
    }

    /// Performs an initialization routine once and only once. The given closure
    /// will be executed if this is the first time `call_once` has been called,
    /// and otherwise the routine will *not* be invoked.
    ///
    /// This method will block the calling thread if another initialization
    /// routine is currently running.
    ///
    /// When this function returns, it is guaranteed that some initialization
    /// has run and completed (it may not be the closure specified). The
    /// returned pointer will point to the result from the closure that was
    /// ran.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(const_fn)]
    /// use spin;
    ///
    /// static INIT: spin::Once<usize> = spin::Once::new();
    ///
    /// fn get_cached_val() -> usize {
    ///     *INIT.call_once(expensive_computation)
    /// }
    ///
    /// fn expensive_computation() -> usize {
    ///     // ...
    /// # 2
    /// }
    /// ```
    pub fn call_once<'a, F>(&'a self, builder: F) -> &'a T
        where F: FnOnce() -> T
    {
        let mut status = self.state.load(Ordering::SeqCst);

        if status == INCOMPLETE {
            status = self.state.compare_and_swap(INCOMPLETE,
                                                 RUNNING,
                                                 Ordering::SeqCst);
            if status == INCOMPLETE { // We init
                // We use a guard (Finish) to catch panics caused by builder
                let mut finish = Finish { state: &self.state, panicked: true };
                unsafe { *self.data.get() = Some(builder()) };
                finish.panicked = false;

                status = COMPLETE;
                self.state.store(status, Ordering::SeqCst);

                // This next line is strictly an optomization
                return self.force_get();
            }
        }

        loop {
            match status {
                INCOMPLETE => unreachable!(),
                RUNNING => { // We spin
                    cpu_relax();
                    status = self.state.load(Ordering::SeqCst)
                },
                PANICKED => panic!("Once has panicked"),
                COMPLETE => return self.force_get(),
                _ => unreachable(),
            }
        }
    }

    /// Returns a pointer iff the `Once` was previously initialized
    pub fn try<'a>(&'a self) -> Option<&'a T> {
        match self.state.load(Ordering::SeqCst) {
            COMPLETE => Some(self.force_get()),
            _        => None,
        }
    }

    /// Like try, but will spin if the `Once` is in the process of being
    /// initialized
    pub fn wait<'a>(&'a self) -> Option<&'a T> {
        loop {
            match self.state.load(Ordering::SeqCst) {
                INCOMPLETE => return None,
                RUNNING    => cpu_relax(), // We spin
                COMPLETE   => return Some(self.force_get()),
                PANICKED   => panic!("Once has panicked"),
                _ => unreachable(),
            }
        }
    }
}

struct Finish<'a> {
    state: &'a AtomicUsize,
    panicked: bool,
}

impl<'a> Drop for Finish<'a> {
    fn drop(&mut self) {
        if self.panicked {
            self.state.store(PANICKED, Ordering::SeqCst);
        }
    }
}

#[cfg(test)]
mod tests {
    use std::prelude::v1::*;

    use std::sync::mpsc::channel;
    use std::thread;
    use super::Once;

    #[test]
    fn smoke_once() {
        static O: Once<()> = Once::new();
        let mut a = 0;
        O.call_once(|| a += 1);
        assert_eq!(a, 1);
        O.call_once(|| a += 1);
        assert_eq!(a, 1);
    }

    #[test]
    fn smoke_once_value() {
        static O: Once<usize> = Once::new();
        let a = O.call_once(|| 1);
        assert_eq!(*a, 1);
        let b = O.call_once(|| 2);
        assert_eq!(*b, 1);
    }

    #[test]
    fn stampede_once() {
        static O: Once<()> = Once::new();
        static mut run: bool = false;

        let (tx, rx) = channel();
        for _ in 0..10 {
            let tx = tx.clone();
            thread::spawn(move|| {
                for _ in 0..4 { thread::yield_now() }
                unsafe {
                    O.call_once(|| {
                        assert!(!run);
                        run = true;
                    });
                    assert!(run);
                }
                tx.send(()).unwrap();
            });
        }

        unsafe {
            O.call_once(|| {
                assert!(!run);
                run = true;
            });
            assert!(run);
        }

        for _ in 0..10 {
            rx.recv().unwrap();
        }
    }

    #[test]
    fn try() {
        static INIT: Once<usize> = Once::new();

        assert!(INIT.try().is_none());
        INIT.call_once(|| 2);
        assert_eq!(INIT.try().map(|r| *r), Some(2));
    }

    #[test]
    fn try_no_wait() {
        static INIT: Once<usize> = Once::new();

        assert!(INIT.try().is_none());
        thread::spawn(move|| {
            INIT.call_once(|| loop { });
        });
        assert!(INIT.try().is_none());
    }

    #[test]
    fn wait() {
        static INIT: Once<usize> = Once::new();

        assert!(INIT.wait().is_none());
        INIT.call_once(|| 3);
        assert_eq!(INIT.wait().map(|r| *r), Some(3));
    }

    #[test]
    fn panic() {
        use ::std::panic;

        static INIT: Once<()> = Once::new();

        // poison the once
        let t = panic::catch_unwind(|| {
            INIT.call_once(|| panic!());
        });
        assert!(t.is_err());

        // poisoning propagates
        let t = panic::catch_unwind(|| {
            INIT.call_once(|| {});
        });
        assert!(t.is_err());
    }
}
<commit_msg>Fix warning for wrongly-named variable<commit_after>use core::cell::UnsafeCell;
use core::sync::atomic::{AtomicUsize, Ordering};

use util::cpu_relax;

/// A synchronization primitive which can be used to run a one-time global
/// initialization. Unlike its std equivalent, this is generalized so that The
/// closure returns a value and it is stored. Once therefore acts something like
/// 1a future, too.
///
/// # Examples
///
/// ```
/// #![feature(const_fn)]
/// use spin;
///
/// static START: spin::Once<()> = spin::Once::new();
///
/// START.call_once(|| {
///     // run initialization here
/// });
/// ```
pub struct Once<T> {
    state: AtomicUsize,
    data: UnsafeCell<Option<T>>, // TODO remove option and use mem::uninitialized
}

// Same unsafe impls as `std::sync::RwLock`, because this also allows for
// concurrent reads.
unsafe impl<T: Send + Sync> Sync for Once<T> {}
unsafe impl<T: Send> Send for Once<T> {}

// Four states that a Once can be in, encoded into the lower bits of `state` in
// the Once structure.
const INCOMPLETE: usize = 0x0;
const RUNNING: usize = 0x1;
const COMPLETE: usize = 0x2;
const PANICKED: usize = 0x3;

#[cfg(feature = "core_intrinsics")]
#[inline(always)]
fn unreachable() -> ! {
    unsafe { ::core::intrinsics::unreachable() }
}

#[cfg(not(feature = "core_intrinsics"))]
#[inline(always)]
fn unreachable() -> ! {
    unreachable!()
}

impl<T> Once<T> {
    /// Creates a new `Once` value.
    pub const fn new() -> Once<T> {
        Once {
            state: AtomicUsize::new(INCOMPLETE),
            data: UnsafeCell::new(None),
        }
    }

    fn force_get<'a>(&'a self) -> &'a T {
        match unsafe { &*self.data.get() }.as_ref() {
            None    => unreachable(),
            Some(p) => p,
        }
    }

    /// Performs an initialization routine once and only once. The given closure
    /// will be executed if this is the first time `call_once` has been called,
    /// and otherwise the routine will *not* be invoked.
    ///
    /// This method will block the calling thread if another initialization
    /// routine is currently running.
    ///
    /// When this function returns, it is guaranteed that some initialization
    /// has run and completed (it may not be the closure specified). The
    /// returned pointer will point to the result from the closure that was
    /// ran.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(const_fn)]
    /// use spin;
    ///
    /// static INIT: spin::Once<usize> = spin::Once::new();
    ///
    /// fn get_cached_val() -> usize {
    ///     *INIT.call_once(expensive_computation)
    /// }
    ///
    /// fn expensive_computation() -> usize {
    ///     // ...
    /// # 2
    /// }
    /// ```
    pub fn call_once<'a, F>(&'a self, builder: F) -> &'a T
        where F: FnOnce() -> T
    {
        let mut status = self.state.load(Ordering::SeqCst);

        if status == INCOMPLETE {
            status = self.state.compare_and_swap(INCOMPLETE,
                                                 RUNNING,
                                                 Ordering::SeqCst);
            if status == INCOMPLETE { // We init
                // We use a guard (Finish) to catch panics caused by builder
                let mut finish = Finish { state: &self.state, panicked: true };
                unsafe { *self.data.get() = Some(builder()) };
                finish.panicked = false;

                status = COMPLETE;
                self.state.store(status, Ordering::SeqCst);

                // This next line is strictly an optomization
                return self.force_get();
            }
        }

        loop {
            match status {
                INCOMPLETE => unreachable!(),
                RUNNING => { // We spin
                    cpu_relax();
                    status = self.state.load(Ordering::SeqCst)
                },
                PANICKED => panic!("Once has panicked"),
                COMPLETE => return self.force_get(),
                _ => unreachable(),
            }
        }
    }

    /// Returns a pointer iff the `Once` was previously initialized
    pub fn try<'a>(&'a self) -> Option<&'a T> {
        match self.state.load(Ordering::SeqCst) {
            COMPLETE => Some(self.force_get()),
            _        => None,
        }
    }

    /// Like try, but will spin if the `Once` is in the process of being
    /// initialized
    pub fn wait<'a>(&'a self) -> Option<&'a T> {
        loop {
            match self.state.load(Ordering::SeqCst) {
                INCOMPLETE => return None,
                RUNNING    => cpu_relax(), // We spin
                COMPLETE   => return Some(self.force_get()),
                PANICKED   => panic!("Once has panicked"),
                _ => unreachable(),
            }
        }
    }
}

struct Finish<'a> {
    state: &'a AtomicUsize,
    panicked: bool,
}

impl<'a> Drop for Finish<'a> {
    fn drop(&mut self) {
        if self.panicked {
            self.state.store(PANICKED, Ordering::SeqCst);
        }
    }
}

#[cfg(test)]
mod tests {
    use std::prelude::v1::*;

    use std::sync::mpsc::channel;
    use std::thread;
    use super::Once;

    #[test]
    fn smoke_once() {
        static O: Once<()> = Once::new();
        let mut a = 0;
        O.call_once(|| a += 1);
        assert_eq!(a, 1);
        O.call_once(|| a += 1);
        assert_eq!(a, 1);
    }

    #[test]
    fn smoke_once_value() {
        static O: Once<usize> = Once::new();
        let a = O.call_once(|| 1);
        assert_eq!(*a, 1);
        let b = O.call_once(|| 2);
        assert_eq!(*b, 1);
    }

    #[test]
    fn stampede_once() {
        static O: Once<()> = Once::new();
        static mut RUN: bool = false;

        let (tx, rx) = channel();
        for _ in 0..10 {
            let tx = tx.clone();
            thread::spawn(move|| {
                for _ in 0..4 { thread::yield_now() }
                unsafe {
                    O.call_once(|| {
                        assert!(!RUN);
                        RUN = true;
                    });
                    assert!(RUN);
                }
                tx.send(()).unwrap();
            });
        }

        unsafe {
            O.call_once(|| {
                assert!(!RUN);
                RUN = true;
            });
            assert!(RUN);
        }

        for _ in 0..10 {
            rx.recv().unwrap();
        }
    }

    #[test]
    fn try() {
        static INIT: Once<usize> = Once::new();

        assert!(INIT.try().is_none());
        INIT.call_once(|| 2);
        assert_eq!(INIT.try().map(|r| *r), Some(2));
    }

    #[test]
    fn try_no_wait() {
        static INIT: Once<usize> = Once::new();

        assert!(INIT.try().is_none());
        thread::spawn(move|| {
            INIT.call_once(|| loop { });
        });
        assert!(INIT.try().is_none());
    }

    #[test]
    fn wait() {
        static INIT: Once<usize> = Once::new();

        assert!(INIT.wait().is_none());
        INIT.call_once(|| 3);
        assert_eq!(INIT.wait().map(|r| *r), Some(3));
    }

    #[test]
    fn panic() {
        use ::std::panic;

        static INIT: Once<()> = Once::new();

        // poison the once
        let t = panic::catch_unwind(|| {
            INIT.call_once(|| panic!());
        });
        assert!(t.is_err());

        // poisoning propagates
        let t = panic::catch_unwind(|| {
            INIT.call_once(|| {});
        });
        assert!(t.is_err());
    }
}
<|endoftext|>


<commit_before>/// A page, can be a blog post or a basic page
use std::cmp::Ordering;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std::result::Result as StdResult;

use pulldown_cmark as cmark;
use regex::Regex;
use tera::{Tera, Context};
use serde::ser::{SerializeStruct, self};
use slug::slugify;

use errors::{Result, ResultExt};
use config::Config;
use front_matter::{FrontMatter};

lazy_static! {
    static ref PAGE_RE: Regex = Regex::new(r"^\n?\+\+\+\n((?s).*(?-s))\+\+\+\n((?s).*(?-s))$").unwrap();
}

#[derive(Clone, Debug, PartialEq, Deserialize)]
pub struct Page {
    /// .md filepath, excluding the content/ bit
    #[serde(skip_serializing)]
    pub filepath: String,
    /// The name of the .md file
    #[serde(skip_serializing)]
    pub filename: String,
    /// The directories above our .md file are called sections
    /// for example a file at content/kb/solutions/blabla.md will have 2 sections:
    /// `kb` and `solutions`
    #[serde(skip_serializing)]
    pub sections: Vec<String>,
    /// The actual content of the page, in markdown
    #[serde(skip_serializing)]
    pub raw_content: String,
    /// The HTML rendered of the page
    pub content: String,
    /// The front matter meta-data
    pub meta: FrontMatter,

    /// The slug of that page.
    /// First tries to find the slug in the meta and defaults to filename otherwise
    pub slug: String,
    /// The relative URL of the page
    pub url: String,
    /// The full URL for that page
    pub permalink: String,

    /// The previous page, by date
    pub previous: Option<Box<Page>>,
    /// The next page, by date
    pub next: Option<Box<Page>>,
}

impl Page {
    pub fn new(meta: FrontMatter) -> Page {
        Page {
            filepath: "".to_string(),
            filename: "".to_string(),
            sections: vec![],
            raw_content: "".to_string(),
            content: "".to_string(),
            slug: "".to_string(),
            url: "".to_string(),
            permalink: "".to_string(),
            meta: meta,
            previous: None,
            next: None,
        }
    }

    // Get word count and estimated reading time
    pub fn get_reading_analytics(&self) -> (usize, usize) {
        // Only works for latin language but good enough for a start
        let word_count: usize = self.raw_content.split_whitespace().count();

        // https://help.medium.com/hc/en-us/articles/214991667-Read-time
        // 275 seems a bit too high though
        (word_count, (word_count / 200))
    }

    // Parse a page given the content of the .md file
    // Files without front matter or with invalid front matter are considered
    // erroneous
    pub fn parse(filepath: &str, content: &str, config: &Config) -> Result<Page> {
        // 1. separate front matter from content
        if !PAGE_RE.is_match(content) {
            bail!("Couldn't find front matter in `{}`. Did you forget to add `+++`?", filepath);
        }

        // 2. extract the front matter and the content
        let caps = PAGE_RE.captures(content).unwrap();
        // caps[0] is the full match
        let front_matter = &caps[1];
        let content = &caps[2];

        // 3. create our page, parse front matter and assign all of that
        let meta = FrontMatter::parse(&front_matter)
            .chain_err(|| format!("Error when parsing front matter of file `{}`", filepath))?;

        let mut page = Page::new(meta);
        page.filepath = filepath.to_string();
        page.raw_content = content.to_string();
        page.content = {
            let mut html = String::new();
            let parser = cmark::Parser::new(&page.raw_content);
            cmark::html::push_html(&mut html, parser);
            html
        };
        let path = Path::new(filepath);
        page.filename = path.file_stem().expect("Couldn't get filename").to_string_lossy().to_string();
        page.slug = {
            if let Some(ref slug) = page.meta.slug {
                slug.to_string()
            } else {
                slugify(page.filename.clone())
            }
        };

        // 4. Find sections
        // Pages with custom urls exists outside of sections
        if let Some(ref u) = page.meta.url {
            page.url = u.to_string();
        } else {
            // find out if we have sections
            for section in path.parent().unwrap().components() {
                page.sections.push(section.as_ref().to_string_lossy().to_string());
            }

            if !page.sections.is_empty() {
                page.url = format!("{}/{}", page.sections.join("/"), page.slug);
            } else {
                page.url = format!("{}", page.slug);
            }
        }
        page.permalink = if config.base_url.ends_with("/") {
            format!("{}{}", config.base_url, page.url)
        } else {
            format!("{}/{}", config.base_url, page.url)
        };

        Ok(page)
    }

    pub fn from_file<P: AsRef<Path>>(path: P, config: &Config) -> Result<Page> {
        let path = path.as_ref();

        let mut content = String::new();
        File::open(path)
            .chain_err(|| format!("Failed to open '{:?}'", path.display()))?
            .read_to_string(&mut content)?;

        // Remove the content string from name
        // Maybe get a path as an arg instead and use strip_prefix?
        Page::parse(&path.strip_prefix("content").unwrap().to_string_lossy(), &content, config)
    }

    fn get_layout_name(&self) -> String {
        match self.meta.layout {
            Some(ref l) => l.to_string(),
            None => "page.html".to_string()
        }
    }

    /// Renders the page using the default layout, unless specified in front-matter
    pub fn render_html(&self, tera: &Tera, config: &Config) -> Result<String> {
        let tpl = self.get_layout_name();
        let mut context = Context::new();
        context.add("site", config);
        context.add("page", self);

        tera.render(&tpl, &context)
            .chain_err(|| "Error while rendering template")
    }
}

impl ser::Serialize for Page {
    fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error> where S: ser::Serializer {
        let mut state = serializer.serialize_struct("page", 13)?;
        state.serialize_field("content", &self.content)?;
        state.serialize_field("title", &self.meta.title)?;
        state.serialize_field("description", &self.meta.description)?;
        state.serialize_field("date", &self.meta.date)?;
        state.serialize_field("slug", &self.slug)?;
        state.serialize_field("url", &format!("/{}", self.url))?;
        state.serialize_field("permalink", &self.permalink)?;
        state.serialize_field("tags", &self.meta.tags)?;
        state.serialize_field("draft", &self.meta.draft)?;
        state.serialize_field("category", &self.meta.category)?;
        state.serialize_field("extra", &self.meta.extra)?;
        let (word_count, reading_time) = self.get_reading_analytics();
        state.serialize_field("word_count", &word_count)?;
        state.serialize_field("reading_time", &reading_time)?;
        state.end()
    }
}

impl PartialOrd for Page {
    fn partial_cmp(&self, other: &Page) -> Option<Ordering> {
        if self.meta.date.is_none() {
            println!("No self data");
            return Some(Ordering::Less);
        }

        if other.meta.date.is_none() {
            println!("No other date");
            return Some(Ordering::Greater);
        }

        let this_date = self.meta.parse_date().unwrap();
        let other_date = other.meta.parse_date().unwrap();

        if this_date > other_date {
            return Some(Ordering::Less);
        }
        if this_date < other_date {
            return Some(Ordering::Greater);
        }

        Some(Ordering::Equal)
    }
}

#[cfg(test)]
mod tests {
    use super::{Page};
    use config::Config;

    #[test]
    fn test_can_parse_a_valid_page() {
        let content = r#"
+++
title = "Hello"
description = "hey there"
slug = "hello-world"
+++
Hello world"#;
        let res = Page::parse("post.md", content, &Config::default());
        assert!(res.is_ok());
        let page = res.unwrap();

        assert_eq!(page.meta.title, "Hello".to_string());
        assert_eq!(page.meta.slug.unwrap(), "hello-world".to_string());
        assert_eq!(page.raw_content, "Hello world".to_string());
        assert_eq!(page.content, "
Hello world
\n".to_string());
    }

    #[test]
    fn test_can_find_one_parent_directory() {
        let content = r#"
+++
title = "Hello"
description = "hey there"
slug = "hello-world"
+++
Hello world"#;
        let res = Page::parse("posts/intro.md", content, &Config::default());
        assert!(res.is_ok());
        let page = res.unwrap();
        assert_eq!(page.sections, vec!["posts".to_string()]);
    }

    #[test]
    fn test_can_find_multiple_parent_directories() {
        let content = r#"
+++
title = "Hello"
description = "hey there"
slug = "hello-world"
+++
Hello world"#;
        let res = Page::parse("posts/intro/start.md", content, &Config::default());
        assert!(res.is_ok());
        let page = res.unwrap();
        assert_eq!(page.sections, vec!["posts".to_string(), "intro".to_string()]);
    }

    #[test]
    fn test_can_make_url_from_sections_and_slug() {
        let content = r#"
+++
title = "Hello"
description = "hey there"
slug = "hello-world"
+++
Hello world"#;
        let mut conf = Config::default();
        conf.base_url = "http://hello.com/".to_string();
        let res = Page::parse("posts/intro/start.md", content, &conf);
        assert!(res.is_ok());
        let page = res.unwrap();
        assert_eq!(page.url, "posts/intro/hello-world");
        assert_eq!(page.permalink, "http://hello.com/posts/intro/hello-world");
    }

    #[test]
    fn test_can_make_permalink_with_non_trailing_slash_base_url() {
        let content = r#"
+++
title = "Hello"
description = "hey there"
slug = "hello-world"
+++
Hello world"#;
        let mut conf = Config::default();
        conf.base_url = "http://hello.com".to_string();
        let res = Page::parse("posts/intro/start.md", content, &conf);
        assert!(res.is_ok());
        let page = res.unwrap();
        assert_eq!(page.url, "posts/intro/hello-world");
        println!("{}", page.permalink);
        assert_eq!(page.permalink, format!("{}{}", conf.base_url, "/posts/intro/hello-world"));
    }

    #[test]
    fn test_can_make_url_from_slug_only() {
        let content = r#"
+++
title = "Hello"
description = "hey there"
slug = "hello-world"
+++
Hello world"#;
        let res = Page::parse("start.md", content, &Config::default());
        assert!(res.is_ok());
        let page = res.unwrap();
        assert_eq!(page.url, "hello-world");
        assert_eq!(page.permalink, format!("{}{}", Config::default().base_url, "hello-world"));
    }

    #[test]
    fn test_errors_on_invalid_front_matter_format() {
        let content = r#"
title = "Hello"
description = "hey there"
slug = "hello-world"
+++
Hello world"#;
        let res = Page::parse("start.md", content, &Config::default());
        assert!(res.is_err());
    }

    #[test]
    fn test_can_make_slug_from_non_slug_filename() {
        let content = r#"
+++
title = "Hello"
description = "hey there"
+++
Hello world"#;
        let res = Page::parse("file with space.md", content, &Config::default());
        assert!(res.is_ok());
        let page = res.unwrap();
        assert_eq!(page.slug, "file-with-space");
        assert_eq!(page.permalink, format!("{}{}", Config::default().base_url, "file-with-space"));
    }

    #[test]
    fn test_reading_analytics_short() {
        let content = r#"
+++
title = "Hello"
description = "hey there"
+++
Hello world"#;
        let res = Page::parse("file with space.md", content, &Config::default());
        assert!(res.is_ok());
        let page = res.unwrap();
        let (word_count, reading_time) = page.get_reading_analytics();
        assert_eq!(word_count, 2);
        assert_eq!(reading_time, 0);
    }

    #[test]
    fn test_reading_analytics_long() {
        let mut content = r#"
+++
title = "Hello"
description = "hey there"
+++
Hello world"#.to_string();
        for _ in 0..1000 {
            content.push_str(" Hello world");
        }
        let res = Page::parse("hello.md", &content, &Config::default());
        assert!(res.is_ok());
        let page = res.unwrap();
        let (word_count, reading_time) = page.get_reading_analytics();
        assert_eq!(word_count, 2002);
        assert_eq!(reading_time, 10);
    }
}
<commit_msg>Add summary<commit_after>/// A page, can be a blog post or a basic page
use std::cmp::Ordering;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std::result::Result as StdResult;

use pulldown_cmark as cmark;
use regex::Regex;
use tera::{Tera, Context};
use serde::ser::{SerializeStruct, self};
use slug::slugify;

use errors::{Result, ResultExt};
use config::Config;
use front_matter::{FrontMatter};

lazy_static! {
    static ref PAGE_RE: Regex = Regex::new(r"^\n?\+\+\+\n((?s).*(?-s))\+\+\+\n((?s).*(?-s))$").unwrap();
    static ref SUMMARY_RE: Regex = Regex::new(r"").unwrap();
}

fn markdown_to_html(content: &str) -> String {
    let mut html = String::new();
    let parser = cmark::Parser::new(content);
    cmark::html::push_html(&mut html, parser);
    html
}

#[derive(Clone, Debug, PartialEq, Deserialize)]
pub struct Page {
    /// .md filepath, excluding the content/ bit
    #[serde(skip_serializing)]
    pub filepath: String,
    /// The name of the .md file
    #[serde(skip_serializing)]
    pub filename: String,
    /// The directories above our .md file are called sections
    /// for example a file at content/kb/solutions/blabla.md will have 2 sections:
    /// `kb` and `solutions`
    #[serde(skip_serializing)]
    pub sections: Vec<String>,
    /// The actual content of the page, in markdown
    #[serde(skip_serializing)]
    pub raw_content: String,
    /// The HTML rendered of the page
    pub content: String,
    /// The front matter meta-data
    pub meta: FrontMatter,

    /// The slug of that page.
    /// First tries to find the slug in the meta and defaults to filename otherwise
    pub slug: String,
    /// The relative URL of the page
    pub url: String,
    /// The full URL for that page
    pub permalink: String,
    /// The summary for the article, defaults to empty string
    /// When  is found in the text, will take the content up to that part
    /// as summary
    pub summary: String,

    /// The previous page, by date
    pub previous: Option<Box<Page>>,
    /// The next page, by date
    pub next: Option<Box<Page>>,
}

impl Page {
    pub fn new(meta: FrontMatter) -> Page {
        Page {
            filepath: "".to_string(),
            filename: "".to_string(),
            sections: vec![],
            raw_content: "".to_string(),
            content: "".to_string(),
            slug: "".to_string(),
            url: "".to_string(),
            permalink: "".to_string(),
            summary: "".to_string(),
            meta: meta,
            previous: None,
            next: None,
        }
    }

    // Get word count and estimated reading time
    pub fn get_reading_analytics(&self) -> (usize, usize) {
        // Only works for latin language but good enough for a start
        let word_count: usize = self.raw_content.split_whitespace().count();

        // https://help.medium.com/hc/en-us/articles/214991667-Read-time
        // 275 seems a bit too high though
        (word_count, (word_count / 200))
    }

    // Parse a page given the content of the .md file
    // Files without front matter or with invalid front matter are considered
    // erroneous
    pub fn parse(filepath: &str, content: &str, config: &Config) -> Result<Page> {
        // 1. separate front matter from content
        if !PAGE_RE.is_match(content) {
            bail!("Couldn't find front matter in `{}`. Did you forget to add `+++`?", filepath);
        }

        // 2. extract the front matter and the content
        let caps = PAGE_RE.captures(content).unwrap();
        // caps[0] is the full match
        let front_matter = &caps[1];
        let content = &caps[2];

        // 3. create our page, parse front matter and assign all of that
        let meta = FrontMatter::parse(&front_matter)
            .chain_err(|| format!("Error when parsing front matter of file `{}`", filepath))?;

        let mut page = Page::new(meta);
        page.filepath = filepath.to_string();
        page.raw_content = content.to_string();
        page.content = markdown_to_html(&page.raw_content);

        if page.raw_content.contains("") {
            page.summary = {
                let summary = SUMMARY_RE.split(&page.raw_content).collect::<Vec<&str>>()[0];
                markdown_to_html(summary)
            }
        }

        let path = Path::new(filepath);
        page.filename = path.file_stem().expect("Couldn't get filename").to_string_lossy().to_string();
        page.slug = {
            if let Some(ref slug) = page.meta.slug {
                slug.to_string()
            } else {
                slugify(page.filename.clone())
            }
        };

        // 4. Find sections
        // Pages with custom urls exists outside of sections
        if let Some(ref u) = page.meta.url {
            page.url = u.to_string();
        } else {
            // find out if we have sections
            for section in path.parent().unwrap().components() {
                page.sections.push(section.as_ref().to_string_lossy().to_string());
            }

            if !page.sections.is_empty() {
                page.url = format!("{}/{}", page.sections.join("/"), page.slug);
            } else {
                page.url = format!("{}", page.slug);
            }
        }
        page.permalink = if config.base_url.ends_with("/") {
            format!("{}{}", config.base_url, page.url)
        } else {
            format!("{}/{}", config.base_url, page.url)
        };

        Ok(page)
    }

    pub fn from_file<P: AsRef<Path>>(path: P, config: &Config) -> Result<Page> {
        let path = path.as_ref();

        let mut content = String::new();
        File::open(path)
            .chain_err(|| format!("Failed to open '{:?}'", path.display()))?
            .read_to_string(&mut content)?;

        // Remove the content string from name
        // Maybe get a path as an arg instead and use strip_prefix?
        Page::parse(&path.strip_prefix("content").unwrap().to_string_lossy(), &content, config)
    }

    fn get_layout_name(&self) -> String {
        match self.meta.layout {
            Some(ref l) => l.to_string(),
            None => "page.html".to_string()
        }
    }

    /// Renders the page using the default layout, unless specified in front-matter
    pub fn render_html(&self, tera: &Tera, config: &Config) -> Result<String> {
        let tpl = self.get_layout_name();
        let mut context = Context::new();
        context.add("site", config);
        context.add("page", self);

        tera.render(&tpl, &context)
            .chain_err(|| "Error while rendering template")
    }
}

impl ser::Serialize for Page {
    fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error> where S: ser::Serializer {
        let mut state = serializer.serialize_struct("page", 13)?;
        state.serialize_field("content", &self.content)?;
        state.serialize_field("title", &self.meta.title)?;
        state.serialize_field("description", &self.meta.description)?;
        state.serialize_field("date", &self.meta.date)?;
        state.serialize_field("slug", &self.slug)?;
        state.serialize_field("url", &format!("/{}", self.url))?;
        state.serialize_field("permalink", &self.permalink)?;
        state.serialize_field("tags", &self.meta.tags)?;
        state.serialize_field("draft", &self.meta.draft)?;
        state.serialize_field("category", &self.meta.category)?;
        state.serialize_field("extra", &self.meta.extra)?;
        let (word_count, reading_time) = self.get_reading_analytics();
        state.serialize_field("word_count", &word_count)?;
        state.serialize_field("reading_time", &reading_time)?;
        state.end()
    }
}

impl PartialOrd for Page {
    fn partial_cmp(&self, other: &Page) -> Option<Ordering> {
        if self.meta.date.is_none() {
            println!("No self data");
            return Some(Ordering::Less);
        }

        if other.meta.date.is_none() {
            println!("No other date");
            return Some(Ordering::Greater);
        }

        let this_date = self.meta.parse_date().unwrap();
        let other_date = other.meta.parse_date().unwrap();

        if this_date > other_date {
            return Some(Ordering::Less);
        }
        if this_date < other_date {
            return Some(Ordering::Greater);
        }

        Some(Ordering::Equal)
    }
}

#[cfg(test)]
mod tests {
    use super::{Page};
    use config::Config;

    #[test]
    fn test_can_parse_a_valid_page() {
        let content = r#"
+++
title = "Hello"
description = "hey there"
slug = "hello-world"
+++
Hello world"#;
        let res = Page::parse("post.md", content, &Config::default());
        assert!(res.is_ok());
        let page = res.unwrap();

        assert_eq!(page.meta.title, "Hello".to_string());
        assert_eq!(page.meta.slug.unwrap(), "hello-world".to_string());
        assert_eq!(page.raw_content, "Hello world".to_string());
        assert_eq!(page.content, "
Hello world
\n".to_string());
    }

    #[test]
    fn test_can_find_one_parent_directory() {
        let content = r#"
+++
title = "Hello"
description = "hey there"
slug = "hello-world"
+++
Hello world"#;
        let res = Page::parse("posts/intro.md", content, &Config::default());
        assert!(res.is_ok());
        let page = res.unwrap();
        assert_eq!(page.sections, vec!["posts".to_string()]);
    }

    #[test]
    fn test_can_find_multiple_parent_directories() {
        let content = r#"
+++
title = "Hello"
description = "hey there"
slug = "hello-world"
+++
Hello world"#;
        let res = Page::parse("posts/intro/start.md", content, &Config::default());
        assert!(res.is_ok());
        let page = res.unwrap();
        assert_eq!(page.sections, vec!["posts".to_string(), "intro".to_string()]);
    }

    #[test]
    fn test_can_make_url_from_sections_and_slug() {
        let content = r#"
+++
title = "Hello"
description = "hey there"
slug = "hello-world"
+++
Hello world"#;
        let mut conf = Config::default();
        conf.base_url = "http://hello.com/".to_string();
        let res = Page::parse("posts/intro/start.md", content, &conf);
        assert!(res.is_ok());
        let page = res.unwrap();
        assert_eq!(page.url, "posts/intro/hello-world");
        assert_eq!(page.permalink, "http://hello.com/posts/intro/hello-world");
    }

    #[test]
    fn test_can_make_permalink_with_non_trailing_slash_base_url() {
        let content = r#"
+++
title = "Hello"
description = "hey there"
slug = "hello-world"
+++
Hello world"#;
        let mut conf = Config::default();
        conf.base_url = "http://hello.com".to_string();
        let res = Page::parse("posts/intro/start.md", content, &conf);
        assert!(res.is_ok());
        let page = res.unwrap();
        assert_eq!(page.url, "posts/intro/hello-world");
        println!("{}", page.permalink);
        assert_eq!(page.permalink, format!("{}{}", conf.base_url, "/posts/intro/hello-world"));
    }

    #[test]
    fn test_can_make_url_from_slug_only() {
        let content = r#"
+++
title = "Hello"
description = "hey there"
slug = "hello-world"
+++
Hello world"#;
        let res = Page::parse("start.md", content, &Config::default());
        assert!(res.is_ok());
        let page = res.unwrap();
        assert_eq!(page.url, "hello-world");
        assert_eq!(page.permalink, format!("{}{}", Config::default().base_url, "hello-world"));
    }

    #[test]
    fn test_errors_on_invalid_front_matter_format() {
        let content = r#"
title = "Hello"
description = "hey there"
slug = "hello-world"
+++
Hello world"#;
        let res = Page::parse("start.md", content, &Config::default());
        assert!(res.is_err());
    }

    #[test]
    fn test_can_make_slug_from_non_slug_filename() {
        let content = r#"
+++
title = "Hello"
description = "hey there"
+++
Hello world"#;
        let res = Page::parse("file with space.md", content, &Config::default());
        assert!(res.is_ok());
        let page = res.unwrap();
        assert_eq!(page.slug, "file-with-space");
        assert_eq!(page.permalink, format!("{}{}", Config::default().base_url, "file-with-space"));
    }

    #[test]
    fn test_reading_analytics_short() {
        let content = r#"
+++
title = "Hello"
description = "hey there"
+++
Hello world"#;
        let res = Page::parse("file with space.md", content, &Config::default());
        assert!(res.is_ok());
        let page = res.unwrap();
        let (word_count, reading_time) = page.get_reading_analytics();
        assert_eq!(word_count, 2);
        assert_eq!(reading_time, 0);
    }

    #[test]
    fn test_reading_analytics_long() {
        let mut content = r#"
+++
title = "Hello"
description = "hey there"
+++
Hello world"#.to_string();
        for _ in 0..1000 {
            content.push_str(" Hello world");
        }
        let res = Page::parse("hello.md", &content, &Config::default());
        assert!(res.is_ok());
        let page = res.unwrap();
        let (word_count, reading_time) = page.get_reading_analytics();
        assert_eq!(word_count, 2002);
        assert_eq!(reading_time, 10);
    }

    #[test]
    fn test_automatic_summary_is_empty_string() {
        let content = r#"
+++
title = "Hello"
description = "hey there"
+++
Hello world"#.to_string();
        let res = Page::parse("hello.md", &content, &Config::default());
        assert!(res.is_ok());
        let page = res.unwrap();
        assert_eq!(page.summary, "");
    }

    #[test]
    fn test_can_specify_summary() {
        let content = r#"
+++
title = "Hello"
description = "hey there"
+++
Hello world

"#.to_string();
        let res = Page::parse("hello.md", &content, &Config::default());
        assert!(res.is_ok());
        let page = res.unwrap();
        assert_eq!(page.summary, "
Hello world
\n");
    }
}
<|endoftext|>