<commit_before>use std::cmp::Ordering;

use libc::{size_t, c_int, c_uint};

// Bounded package merge algorithm, based on the paper
// "A Fast and Space-Economical Algorithm for Length-Limited Coding
// Jyrki Katajainen, Alistair Moffat, Andrew Turpin".

/// Nodes forming chains.
#[repr(C)]
pub struct Node {
  weight: size_t,     // Total weight (symbol count) of this chain.
  tail: *const Node,  // Previous node(s) of this chain, or 0 if none.
  count: c_int,       // Leaf symbol index, or number of leaves before this chain.
}

#[repr(C)]
pub struct Leaf {
  weight: size_t,     // Total weight (symbol count) of this chain.
  count: c_int,       // Leaf symbol index, or number of leaves before this chain.
}

/// Memory pool for nodes.
#[repr(C)]
pub struct NodePool {
  next: *const Node,   // Pointer to a possibly free node in the pool.
}

/// Initializes a chain node with the given values and marks it as in use.
#[no_mangle]
#[allow(non_snake_case)]
pub extern fn InitNode(weight: size_t, count: c_int, tail: *const Node, node_ptr: *mut Node) {
    let node = unsafe {
        assert!(!node_ptr.is_null());
        &mut *node_ptr
    };

    node.weight = weight;
    node.count = count;
    node.tail = tail;
}

/// Converts result of boundary package-merge to the bitlengths. The result in the
/// last chain of the last list contains the amount of active leaves in each list.
/// chain: Chain to extract the bit length from (last chain from last list).
#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ExtractBitLengths(chain: *const Node, leaves: *const Leaf, bitlengths: *mut c_uint) {
    let mut counts = [0; 16];
    let mut end = 16;
    let mut ptr = 15;
    let mut value = 1;

    let mut node_ptr = chain;
    while !node_ptr.is_null() {
        let node = unsafe {
           &*node_ptr
        };

        end -= 1;
        counts[end] = node.count;

        node_ptr = node.tail;
    }

    let mut val = counts[15];
    while ptr >= end {
        while val > counts[ptr - 1] {
            unsafe {
                let leaf = &*leaves.offset((val - 1) as isize);
                *bitlengths.offset(leaf.count as isize) = value;
            }
            val -= 1;
        }
        ptr -= 1;
        value += 1;
    }
}

// #[derive(Clone, Copy, Debug)]
#[derive(Clone, Debug)]
struct N {
    weight: size_t,
    leaf_counts: Vec<c_int>,
}

#[derive(Debug)]
struct L {
    pub weight: size_t,
    pub index: size_t,
}
impl PartialEq for L {
    fn eq(&self, other: &Self) -> bool {
        self.weight == other.weight
    }
}
impl Eq for L { }
impl Ord for L {
    fn cmp(&self, other: &Self) -> Ordering {
        self.weight.cmp(&other.weight)
    }
}
impl PartialOrd for L {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

#[derive(Debug)]
struct List {
    lookahead1: N,
    lookahead2: N,
    next_leaf_index: size_t,
}

pub fn length_limited_code_lengths(frequencies: &[size_t], maxbits: c_int) -> Vec<size_t> {
    let mut leaves = vec![];

    // Count used symbols and place them in the leaves.
    for (i, &freq) in frequencies.iter().enumerate() {
        if freq != 0 {
            leaves.push(L { weight: freq, index: i });
        }
    }

    // Sort the leaves from least frequent to most frequent.
    // Add index into the same variable for stable sorting.
    for leaf in leaves.iter_mut() {
        leaf.weight = (leaf.weight << 9) | leaf.index;
    }
    leaves.sort();
    for leaf in leaves.iter_mut() {
        leaf.weight >>= 9;
    }

    let mut lists = Vec::with_capacity(maxbits as usize);
    for _ in 0..maxbits {
        lists.push(List {
            lookahead1: N { weight: leaves[0].weight, leaf_counts: vec![1] },
            lookahead2: N { weight: leaves[1].weight, leaf_counts: vec![2] },
            next_leaf_index: 2,
        });
    }

    // In the last list, 2 * numsymbols - 2 active chains need to be created. Two
    // are already created in the initialization. Each boundary_pm run creates one.
    let num_boundary_pm_runs = 2 * leaves.len() - 4;
    for _ in 0..num_boundary_pm_runs {
        lists = boundary_pm(lists, &leaves);
    }

    let n = frequencies.len();
    let mut result = vec![0; n];

    let mut a = lists.pop().unwrap().lookahead2.leaf_counts.into_iter().rev().peekable();

    let mut bitlength_value = 1;
    while let Some(leaf_count) = a.next() {
        println!("leaf_count = {:?}", leaf_count);
        let next_count = *a.peek().unwrap_or(&0);
        println!("next_count = {:?}", next_count);
        for i in next_count..leaf_count {
            println!("i = {:?}", i);
            println!("leaves[i as usize] = {:?}", leaves[i as usize]);
            result[leaves[i as usize].index as usize] = bitlength_value;
            println!("result = {:?}", result);
        }
        bitlength_value += 1;
    }

    result
}

fn boundary_pm(mut lists: Vec<List>, leaves: &Vec<L>) -> Vec<List> {
    let mut current_list = lists.pop().unwrap();
    if lists.is_empty() && current_list.next_leaf_index == leaves.len() {
        // We've added all the leaves to the lowest list, so we're done here
        lists.push(current_list);
        return lists;
    }

    current_list.lookahead1 = current_list.lookahead2;

    if lists.is_empty() {
        // We're in the lowest list, just add another leaf to the lookaheads
        // There will always be more leaves to be added on level 0 so this is safe.
        let ref next_leaf = leaves[current_list.next_leaf_index];
        current_list.lookahead2 = N {
            weight: next_leaf.weight,
            leaf_counts: vec![current_list.lookahead1.leaf_counts.last().unwrap() + 1],
        };
        current_list.next_leaf_index += 1;
        lists.push(current_list);
    } else {
        // We're at a list other than the lowest list.
        let previous_list = lists.pop().unwrap();
        let weight_sum = previous_list.lookahead1.weight + previous_list.lookahead2.weight;

        if current_list.next_leaf_index < leaves.len() && weight_sum > leaves[current_list.next_leaf_index].weight {
            // The next leaf goes next; counting itself makes the leaf_count increase by one.
            let mut last_leaf_counts = current_list.lookahead1.leaf_counts.clone();
            let mut last_count = last_leaf_counts.pop().unwrap();
            last_count += 1;
            last_leaf_counts.push(last_count);
            current_list.lookahead2 = N {
                weight: leaves[current_list.next_leaf_index].weight,
                leaf_counts: last_leaf_counts,
            };
            current_list.next_leaf_index += 1;
            lists.push(previous_list);
            lists.push(current_list);
        } else {
            // Make a tree from the lookaheads from the previous list; that goes next.
            // This is not a leaf node, so the leaf count stays the same.
            let mut last_leaf_counts = previous_list.lookahead2.leaf_counts.clone();
            last_leaf_counts.push(*current_list.lookahead1.leaf_counts.last().unwrap());
            current_list.lookahead2 = N {
                weight: weight_sum,
                leaf_counts: last_leaf_counts,
            };
            // The previous list needs two new lookahead nodes.
            lists.push(previous_list);
            lists = boundary_pm(lists, leaves);
            lists = boundary_pm(lists, leaves);
            lists.push(current_list);
        }
    }

    lists
}

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

    #[test]
    fn test_from_paper_3() {
        let input = [1, 1, 5, 7, 10, 14];
        let output = length_limited_code_lengths(&input, 3);
        let answer = vec![3, 3, 3, 3, 2, 2];
        assert_eq!(output, answer);
    }

    #[test]
    fn test_from_paper_4() {
        let input = [1, 1, 5, 7, 10, 14];
        let output = length_limited_code_lengths(&input, 4);
        let answer = vec![4, 4, 3, 2, 2, 2];
        assert_eq!(output, answer);
    }

    // #[test]
    // fn one_test() {
    //     let input = [252, 0, 1, 6, 9, 10, 6, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    //     let output = length_limited_code_lengths(&input, 7);
    //     let answer = vec![1, 0, 6, 4, 3, 3, 3, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    //     assert_eq!(output, answer);
    // }
}

// maxbits: 7
// frequencies: [252, 0, 1, 6, 9, 10, 6, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]
// bitlengths: [1, 0, 6, 4, 3, 3, 3, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]
// maxbits: 7
// frequencies: [10, 0, 1, 6, 9, 4, 6, 3, 2, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, ]
// bitlengths: [3, 0, 6, 4, 3, 4, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, ]
// maxbits: 7
// frequencies: [4, 0, 1, 6, 9, 10, 6, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 26, 0, ]
// bitlengths: [4, 0, 6, 4, 3, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, ]
// maxbits: 7
// frequencies: [4, 0, 1, 6, 9, 4, 6, 3, 2, 0, 0, 0, 0, 0, 0, 0, 1, 26, 0, ]
// bitlengths: [4, 0, 6, 4, 3, 4, 4, 4, 5, 0, 0, 0, 0, 0, 0, 0, 6, 1, 0, ]
// maxbits: 7
// frequencies: [19, 0, 1, 6, 9, 10, 6, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, ]
// bitlengths: [2, 0, 6, 3, 3, 2, 3, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, ]
// maxbits: 7
// frequencies: [8, 0, 1, 6, 9, 4, 6, 3, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, ]
// bitlengths: [3, 0, 5, 3, 2, 3, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, ]
// maxbits: 7
// frequencies: [4, 0, 1, 6, 9, 10, 6, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, ]
// bitlengths: [4, 0, 6, 3, 2, 2, 3, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 5, 4, ]
// maxbits: 7
// frequencies: [4, 0, 1, 6, 9, 4, 6, 3, 2, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, ]
// bitlengths: [3, 0, 6, 3, 2, 3, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 6, 4, 4, ]
// maxbits: 15
// frequencies: [0, 0, 0, 0, 0, 0, 18, 0, 6, 0, 12, 2, 14, 9, 27, 15, 23, 15, 17, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]
// bitlengths: [0, 0, 0, 0, 0, 0, 3, 0, 5, 0, 4, 6, 4, 4, 3, 4, 3, 3, 3, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]
<commit_msg>Enable more tests, remove printlns<commit_after>use std::cmp::Ordering;

use libc::{size_t, c_int, c_uint};

// Bounded package merge algorithm, based on the paper
// "A Fast and Space-Economical Algorithm for Length-Limited Coding
// Jyrki Katajainen, Alistair Moffat, Andrew Turpin".

/// Nodes forming chains.
#[repr(C)]
pub struct Node {
  weight: size_t,     // Total weight (symbol count) of this chain.
  tail: *const Node,  // Previous node(s) of this chain, or 0 if none.
  count: c_int,       // Leaf symbol index, or number of leaves before this chain.
}

#[repr(C)]
pub struct Leaf {
  weight: size_t,     // Total weight (symbol count) of this chain.
  count: c_int,       // Leaf symbol index, or number of leaves before this chain.
}

/// Memory pool for nodes.
#[repr(C)]
pub struct NodePool {
  next: *const Node,   // Pointer to a possibly free node in the pool.
}

/// Initializes a chain node with the given values and marks it as in use.
#[no_mangle]
#[allow(non_snake_case)]
pub extern fn InitNode(weight: size_t, count: c_int, tail: *const Node, node_ptr: *mut Node) {
    let node = unsafe {
        assert!(!node_ptr.is_null());
        &mut *node_ptr
    };

    node.weight = weight;
    node.count = count;
    node.tail = tail;
}

/// Converts result of boundary package-merge to the bitlengths. The result in the
/// last chain of the last list contains the amount of active leaves in each list.
/// chain: Chain to extract the bit length from (last chain from last list).
#[no_mangle]
#[allow(non_snake_case)]
pub extern fn ExtractBitLengths(chain: *const Node, leaves: *const Leaf, bitlengths: *mut c_uint) {
    let mut counts = [0; 16];
    let mut end = 16;
    let mut ptr = 15;
    let mut value = 1;

    let mut node_ptr = chain;
    while !node_ptr.is_null() {
        let node = unsafe {
           &*node_ptr
        };

        end -= 1;
        counts[end] = node.count;

        node_ptr = node.tail;
    }

    let mut val = counts[15];
    while ptr >= end {
        while val > counts[ptr - 1] {
            unsafe {
                let leaf = &*leaves.offset((val - 1) as isize);
                *bitlengths.offset(leaf.count as isize) = value;
            }
            val -= 1;
        }
        ptr -= 1;
        value += 1;
    }
}

// #[derive(Clone, Copy, Debug)]
#[derive(Clone, Debug)]
struct N {
    weight: size_t,
    leaf_counts: Vec<c_int>,
}

#[derive(Debug)]
struct L {
    pub weight: size_t,
    pub index: size_t,
}
impl PartialEq for L {
    fn eq(&self, other: &Self) -> bool {
        self.weight == other.weight
    }
}
impl Eq for L { }
impl Ord for L {
    fn cmp(&self, other: &Self) -> Ordering {
        self.weight.cmp(&other.weight)
    }
}
impl PartialOrd for L {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

#[derive(Debug)]
struct List {
    lookahead1: N,
    lookahead2: N,
    next_leaf_index: size_t,
}

pub fn length_limited_code_lengths(frequencies: &[size_t], maxbits: c_int) -> Vec<size_t> {
    let mut leaves = vec![];

    // Count used symbols and place them in the leaves.
    for (i, &freq) in frequencies.iter().enumerate() {
        if freq != 0 {
            leaves.push(L { weight: freq, index: i });
        }
    }

    // Sort the leaves from least frequent to most frequent.
    // Add index into the same variable for stable sorting.
    for leaf in leaves.iter_mut() {
        leaf.weight = (leaf.weight << 9) | leaf.index;
    }
    leaves.sort();
    for leaf in leaves.iter_mut() {
        leaf.weight >>= 9;
    }

    let mut lists = Vec::with_capacity(maxbits as usize);
    for _ in 0..maxbits {
        lists.push(List {
            lookahead1: N { weight: leaves[0].weight, leaf_counts: vec![1] },
            lookahead2: N { weight: leaves[1].weight, leaf_counts: vec![2] },
            next_leaf_index: 2,
        });
    }

    // In the last list, 2 * numsymbols - 2 active chains need to be created. Two
    // are already created in the initialization. Each boundary_pm run creates one.
    let num_boundary_pm_runs = 2 * leaves.len() - 4;
    for _ in 0..num_boundary_pm_runs {
        lists = boundary_pm(lists, &leaves);
    }

    let n = frequencies.len();
    let mut result = vec![0; n];

    let mut a = lists.pop().unwrap().lookahead2.leaf_counts.into_iter().rev().peekable();

    let mut bitlength_value = 1;
    while let Some(leaf_count) = a.next() {
        let next_count = *a.peek().unwrap_or(&0);
        for i in next_count..leaf_count {
            result[leaves[i as usize].index as usize] = bitlength_value;
        }
        bitlength_value += 1;
    }

    result
}

fn boundary_pm(mut lists: Vec<List>, leaves: &Vec<L>) -> Vec<List> {
    let mut current_list = lists.pop().unwrap();
    if lists.is_empty() && current_list.next_leaf_index == leaves.len() {
        // We've added all the leaves to the lowest list, so we're done here
        lists.push(current_list);
        return lists;
    }

    current_list.lookahead1 = current_list.lookahead2;

    if lists.is_empty() {
        // We're in the lowest list, just add another leaf to the lookaheads
        // There will always be more leaves to be added on level 0 so this is safe.
        let ref next_leaf = leaves[current_list.next_leaf_index];
        current_list.lookahead2 = N {
            weight: next_leaf.weight,
            leaf_counts: vec![current_list.lookahead1.leaf_counts.last().unwrap() + 1],
        };
        current_list.next_leaf_index += 1;
        lists.push(current_list);
    } else {
        // We're at a list other than the lowest list.
        let previous_list = lists.pop().unwrap();
        let weight_sum = previous_list.lookahead1.weight + previous_list.lookahead2.weight;

        if current_list.next_leaf_index < leaves.len() && weight_sum > leaves[current_list.next_leaf_index].weight {
            // The next leaf goes next; counting itself makes the leaf_count increase by one.
            let mut last_leaf_counts = current_list.lookahead1.leaf_counts.clone();
            let mut last_count = last_leaf_counts.pop().unwrap();
            last_count += 1;
            last_leaf_counts.push(last_count);
            current_list.lookahead2 = N {
                weight: leaves[current_list.next_leaf_index].weight,
                leaf_counts: last_leaf_counts,
            };
            current_list.next_leaf_index += 1;
            lists.push(previous_list);
            lists.push(current_list);
        } else {
            // Make a tree from the lookaheads from the previous list; that goes next.
            // This is not a leaf node, so the leaf count stays the same.
            let mut last_leaf_counts = previous_list.lookahead2.leaf_counts.clone();
            last_leaf_counts.push(*current_list.lookahead1.leaf_counts.last().unwrap());
            current_list.lookahead2 = N {
                weight: weight_sum,
                leaf_counts: last_leaf_counts,
            };
            // The previous list needs two new lookahead nodes.
            lists.push(previous_list);
            lists = boundary_pm(lists, leaves);
            lists = boundary_pm(lists, leaves);
            lists.push(current_list);
        }
    }

    lists
}

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

    #[test]
    fn test_from_paper_3() {
        let input = [1, 1, 5, 7, 10, 14];
        let output = length_limited_code_lengths(&input, 3);
        let answer = vec![3, 3, 3, 3, 2, 2];
        assert_eq!(output, answer);
    }

    #[test]
    fn test_from_paper_4() {
        let input = [1, 1, 5, 7, 10, 14];
        let output = length_limited_code_lengths(&input, 4);
        let answer = vec![4, 4, 3, 2, 2, 2];
        assert_eq!(output, answer);
    }

    #[test]
    fn maxbits_7() {
        let input = [252, 0, 1, 6, 9, 10, 6, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 7);
        let answer = vec![1, 0, 6, 4, 3, 3, 3, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }

    #[test]
    fn maxbits_15() {
        let input = [0, 0, 0, 0, 0, 0, 18, 0, 6, 0, 12, 2, 14, 9, 27, 15, 23, 15, 17, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        let output = length_limited_code_lengths(&input, 15);
        let answer = vec! [0, 0, 0, 0, 0, 0, 3, 0, 5, 0, 4, 6, 4, 4, 3, 4, 3, 3, 3, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        assert_eq!(output, answer);
    }
}
<|endoftext|>


<commit_before>// vim: tw=80
use super::*;

use quote::ToTokens;

use crate::{
    mock_function::MockFunction,
    mock_trait::MockTrait
};

fn phantom_default_inits(generics: &Generics) -> Vec<TokenStream> {
    generics.params
    .iter()
    .enumerate()
    .map(|(count, _param)| {
        let phident = format_ident!("_t{}", count);
        quote!(#phident: ::std::marker::PhantomData)
    }).collect()
}

/// Generate any PhantomData field definitions
fn phantom_fields(generics: &Generics) -> Vec<TokenStream> {
    generics.params
    .iter()
    .enumerate()
    .filter_map(|(count, param)| {
        let phident = format_ident!("_t{}", count);
        match param {
            syn::GenericParam::Lifetime(l) => {
                if !l.bounds.is_empty() {
                    compile_error(l.bounds.span(),
                        "#automock does not yet support lifetime bounds on structs");
                }
                let lifetime = &l.lifetime;
                Some(
                quote!(#phident: ::std::marker::PhantomData<&#lifetime ()>)
                )
            },
            syn::GenericParam::Type(tp) => {
                let ty = &tp.ident;
                Some(
                quote!(#phident: ::std::marker::PhantomData<#ty>)
                )
            },
            syn::GenericParam::Const(_) => {
                compile_error(param.span(),
                    "#automock does not yet support generic constants");
                None
            }
        }
    }).collect()
}

/// A collection of methods defined in one spot
struct Methods(Vec<MockFunction>);

impl Methods {
    fn checkpoints(&self) -> Vec<impl ToTokens> {
        self.0.iter()
            .filter(|meth| !meth.is_static())
            .map(|meth| meth.checkpoint())
            .collect::<Vec<_>>()
    }

    /// Return a fragment of code to initialize struct fields during default()
    fn default_inits(&self) -> Vec<TokenStream> {
        self.0.iter()
            .filter(|meth| !meth.is_static())
            .map(|meth| {
                let name = meth.name();
                let attrs = meth.format_attrs(false);
                quote!(#attrs #name: Default::default())
            }).collect::<Vec<_>>()
    }

    fn field_definitions(&self, modname: &Ident) -> Vec<TokenStream> {
        self.0.iter()
            .filter(|meth| !meth.is_static())
            .map(|meth| meth.field_definition(Some(modname)))
            .collect::<Vec<_>>()
    }

    fn priv_mods(&self) -> Vec<impl ToTokens> {
        self.0.iter()
            .map(|meth| meth.priv_module())
            .collect::<Vec<_>>()
    }
}

pub(crate) struct MockItemStruct {
    attrs: Vec<Attribute>,
    generics: Generics,
    /// Does the original struct have a `new` method?
    has_new: bool,
    /// Inherent methods of the mock struct
    methods: Methods,
    /// Name of the overall module that holds all of the mock stuff
    // TODO: base this on name so we can get rid of MockableStruct.original_name
    modname: Ident,
    name: Ident,
    /// Is this a whole MockStruct or just a substructure for a trait impl?
    traits: Vec<MockTrait>,
    vis: Visibility,
}

impl MockItemStruct {
    fn new_method(&self) -> impl ToTokens {
        if self.has_new {
            TokenStream::new()
        } else {
            quote!(
                /// Create a new mock object with no expectations.
                ///
                /// This method will not be generated if the real struct
                /// already has a `new` method.  However, it *will* be
                /// generated if the struct implements a trait with a `new`
                /// method.  The trait's `new` method can still be called
                /// like `<MockX as TraitY>::new`
                pub fn new() -> Self {
                    Self::default()
                }
            )
        }
    }

    fn phantom_default_inits(&self) -> Vec<TokenStream> {
        phantom_default_inits(&self.generics)
    }

    fn phantom_fields(&self) -> Vec<TokenStream> {
        phantom_fields(&self.generics)
    }
}

impl From<MockableStruct> for MockItemStruct {
    fn from(mockable: MockableStruct) -> MockItemStruct {
        let mock_ident = gen_mod_ident(&mockable.name, None);
        let modname = gen_mod_ident(&mockable.original_name, None);
        let generics = mockable.generics.clone();
        let struct_name = &mockable.name;
        let vis = mockable.vis;
        let has_new = mockable.methods.iter()
            .any(|meth| meth.sig.ident == "new") ||
            mockable.traits.iter()
            .any(|trait_|
                trait_.items.iter()
                    .any(|ti| if let TraitItem::Method(tim) = ti {
                            tim.sig.ident == "new"
                        } else {
                            false
                        }
                    )
            );
        let methods = Methods(mockable.methods.into_iter()
            .map(|meth|
                mock_function::Builder::new(&meth.sig, &meth.vis)
                    .attrs(&meth.attrs)
                    .parent(&mock_ident)
                    .struct_(&struct_name)
                    .struct_generics(&generics)
                    .levels(2)
                    .call_levels(0)
                    .build()
            ).collect::<Vec<_>>());
        let structname = &mockable.name;
        let traits = mockable.traits.into_iter()
            .map(|t| MockTrait::new(structname, &generics, t, &vis))
            .collect();

        MockItemStruct {
            attrs: mockable.attrs,
            generics,
            has_new,
            methods,
            modname,
            name: mockable.name,
            traits,
            vis
        }
    }
}

impl ToTokens for MockItemStruct {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let attrs = &self.attrs;
        let struct_name = &self.name;
        let (ig, tg, wc) = self.generics.split_for_impl();
        let modname = &self.modname;
        let calls = self.methods.0.iter()
            .map(|meth| meth.call(Some(modname)))
            .collect::<Vec<_>>();
        let contexts = self.methods.0.iter()
            .filter(|meth| meth.is_static())
            .map(|meth| meth.context_fn(Some(modname)))
            .collect::<Vec<_>>();
        let expects = self.methods.0.iter()
            .filter(|meth| !meth.is_static())
            .map(|meth| meth.expect(modname))
            .collect::<Vec<_>>();
        let method_checkpoints = self.methods.checkpoints();
        let new_method = self.new_method();
        let priv_mods = self.methods.priv_mods();
        let substructs = self.traits.iter()
            .map(|trait_| {
                MockItemTraitImpl {
                    attrs: trait_.attrs.clone(),
                    generics: self.generics.clone(),
                    fieldname: format_ident!("{}_expectations", trait_.name()),
                    methods: Methods(trait_.methods.clone()),
                    modname: format_ident!("{}_{}", &self.modname, trait_.name()),
                    name: format_ident!("{}_{}", &self.name, trait_.name()),
                }
            }).collect::<Vec<_>>();
        let substruct_expectations = self.traits.iter()
            .map(|trait_| format_ident!("{}_expectations", trait_.name()))
            .collect::<Vec<_>>();
        let mut field_definitions = substructs.iter()
            .map(|ss| {
                let fieldname = &ss.fieldname;
                let tyname = &ss.name;
                quote!(#fieldname: #tyname #tg)
            }).collect::<Vec<_>>();
        field_definitions.extend(self.methods.field_definitions(&modname));
        field_definitions.extend(self.phantom_fields());
        let mut default_inits = substructs.iter()
            .map(|ss| {
                let fieldname = &ss.fieldname;
                quote!(#fieldname: Default::default())
            }).collect::<Vec<_>>();
        default_inits.extend(self.methods.default_inits());
        default_inits.extend(self.phantom_default_inits());
        let trait_impls = self.traits.iter()
            .map(|ss| {
                let modname = format_ident!("{}_{}", &self.modname, ss.name());
                ss.trait_impl(&modname)
            }).collect::<Vec<_>>();
        let vis = &self.vis;
        quote!(
            #[allow(non_snake_case)]
            #[allow(missing_docs)]
            pub mod #modname {
                use super::*;
                #(#priv_mods)*
            }
            #[allow(non_camel_case_types)]
            #[allow(non_snake_case)]
            #[allow(missing_docs)]
            #(#attrs)*
            #vis struct #struct_name #ig #wc
            {
                #(#field_definitions),*
            }
            impl #ig ::std::default::Default for #struct_name #tg #wc {
                fn default() -> Self {
                    Self {
                        #(#default_inits),*
                    }
                }
            }
            #(#substructs)*
            impl #ig #struct_name #tg #wc {
                #(#calls)*
                #(#contexts)*
                #(#expects)*
                /// Validate that all current expectations for all methods have
                /// been satisfied, and discard them.
                pub fn checkpoint(&mut self) {
                    #(self.#substruct_expectations.checkpoint();)*
                    #(#method_checkpoints)*
                }
                #new_method
            }
            #(#trait_impls)*
        ).to_tokens(tokens);
    }
}

pub(crate) struct MockItemTraitImpl {
    attrs: Vec<Attribute>,
    generics: Generics,
    /// Inherent methods of the mock struct
    methods: Methods,
    /// Name of the overall module that holds all of the mock stuff
    // TODO: base this on name so we can get rid of MockableStruct.original_name
    modname: Ident,
    name: Ident,
    /// Name of the field of this type in the parent's structure
    fieldname: Ident,
}

impl MockItemTraitImpl {
    fn phantom_default_inits(&self) -> Vec<TokenStream> {
        phantom_default_inits(&self.generics)
    }

    fn phantom_fields(&self) -> Vec<TokenStream> {
        phantom_fields(&self.generics)
    }
}

impl ToTokens for MockItemTraitImpl {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let attrs_nodocs = format_attrs(&self.attrs, false);
        let struct_name = &self.name;
        let (ig, tg, wc) = self.generics.split_for_impl();
        let modname = &self.modname;
        let method_checkpoints = self.methods.checkpoints();
        let mut default_inits = self.methods.default_inits();
        default_inits.extend(self.phantom_default_inits());
        let mut field_definitions = self.methods.field_definitions(&modname);
        field_definitions.extend(self.phantom_fields());
        let priv_mods = self.methods.priv_mods();
        quote!(
            #[allow(non_snake_case)]
            #[allow(missing_docs)]
            pub mod #modname {
                use super::*;
                #(#priv_mods)*
            }
            #[allow(non_camel_case_types)]
            #[allow(non_snake_case)]
            #[allow(missing_docs)]
            #attrs_nodocs
            struct #struct_name #ig #wc
            {
                #(#field_definitions),*
            }
            impl #ig ::std::default::Default for #struct_name #tg #wc {
                fn default() -> Self {
                    Self {
                        #(#default_inits),*
                    }
                }
            }
            impl #ig #struct_name #tg #wc {
                /// Validate that all current expectations for all methods have
                /// been satisfied, and discard them.
                pub fn checkpoint(&mut self) {
                    #(#method_checkpoints)*
                }
            }
        ).to_tokens(tokens);
    }
}
<commit_msg>Delete some dead code.  No change to generated code.<commit_after>// vim: tw=80
use super::*;

use quote::ToTokens;

use crate::{
    mock_function::MockFunction,
    mock_trait::MockTrait
};

fn phantom_default_inits(generics: &Generics) -> Vec<TokenStream> {
    generics.params
    .iter()
    .enumerate()
    .map(|(count, _param)| {
        let phident = format_ident!("_t{}", count);
        quote!(#phident: ::std::marker::PhantomData)
    }).collect()
}

/// Generate any PhantomData field definitions
fn phantom_fields(generics: &Generics) -> Vec<TokenStream> {
    generics.params
    .iter()
    .enumerate()
    .filter_map(|(count, param)| {
        let phident = format_ident!("_t{}", count);
        match param {
            syn::GenericParam::Lifetime(l) => {
                if !l.bounds.is_empty() {
                    compile_error(l.bounds.span(),
                        "#automock does not yet support lifetime bounds on structs");
                }
                let lifetime = &l.lifetime;
                Some(
                quote!(#phident: ::std::marker::PhantomData<&#lifetime ()>)
                )
            },
            syn::GenericParam::Type(tp) => {
                let ty = &tp.ident;
                Some(
                quote!(#phident: ::std::marker::PhantomData<#ty>)
                )
            },
            syn::GenericParam::Const(_) => {
                compile_error(param.span(),
                    "#automock does not yet support generic constants");
                None
            }
        }
    }).collect()
}

/// A collection of methods defined in one spot
struct Methods(Vec<MockFunction>);

impl Methods {
    fn checkpoints(&self) -> Vec<impl ToTokens> {
        self.0.iter()
            .filter(|meth| !meth.is_static())
            .map(|meth| meth.checkpoint())
            .collect::<Vec<_>>()
    }

    /// Return a fragment of code to initialize struct fields during default()
    fn default_inits(&self) -> Vec<TokenStream> {
        self.0.iter()
            .filter(|meth| !meth.is_static())
            .map(|meth| {
                let name = meth.name();
                let attrs = meth.format_attrs(false);
                quote!(#attrs #name: Default::default())
            }).collect::<Vec<_>>()
    }

    fn field_definitions(&self, modname: &Ident) -> Vec<TokenStream> {
        self.0.iter()
            .filter(|meth| !meth.is_static())
            .map(|meth| meth.field_definition(Some(modname)))
            .collect::<Vec<_>>()
    }

    fn priv_mods(&self) -> Vec<impl ToTokens> {
        self.0.iter()
            .map(|meth| meth.priv_module())
            .collect::<Vec<_>>()
    }
}

pub(crate) struct MockItemStruct {
    attrs: Vec<Attribute>,
    generics: Generics,
    /// Does the original struct have a `new` method?
    has_new: bool,
    /// Inherent methods of the mock struct
    methods: Methods,
    /// Name of the overall module that holds all of the mock stuff
    // TODO: base this on name so we can get rid of MockableStruct.original_name
    modname: Ident,
    name: Ident,
    /// Is this a whole MockStruct or just a substructure for a trait impl?
    traits: Vec<MockTrait>,
    vis: Visibility,
}

impl MockItemStruct {
    fn new_method(&self) -> impl ToTokens {
        if self.has_new {
            TokenStream::new()
        } else {
            quote!(
                /// Create a new mock object with no expectations.
                ///
                /// This method will not be generated if the real struct
                /// already has a `new` method.  However, it *will* be
                /// generated if the struct implements a trait with a `new`
                /// method.  The trait's `new` method can still be called
                /// like `<MockX as TraitY>::new`
                pub fn new() -> Self {
                    Self::default()
                }
            )
        }
    }

    fn phantom_default_inits(&self) -> Vec<TokenStream> {
        phantom_default_inits(&self.generics)
    }

    fn phantom_fields(&self) -> Vec<TokenStream> {
        phantom_fields(&self.generics)
    }
}

impl From<MockableStruct> for MockItemStruct {
    fn from(mockable: MockableStruct) -> MockItemStruct {
        let modname = gen_mod_ident(&mockable.original_name, None);
        let generics = mockable.generics.clone();
        let struct_name = &mockable.name;
        let vis = mockable.vis;
        let has_new = mockable.methods.iter()
            .any(|meth| meth.sig.ident == "new") ||
            mockable.traits.iter()
            .any(|trait_|
                trait_.items.iter()
                    .any(|ti| if let TraitItem::Method(tim) = ti {
                            tim.sig.ident == "new"
                        } else {
                            false
                        }
                    )
            );
        let methods = Methods(mockable.methods.into_iter()
            .map(|meth|
                mock_function::Builder::new(&meth.sig, &meth.vis)
                    .attrs(&meth.attrs)
                    .struct_(&struct_name)
                    .struct_generics(&generics)
                    .levels(2)
                    .call_levels(0)
                    .build()
            ).collect::<Vec<_>>());
        let structname = &mockable.name;
        let traits = mockable.traits.into_iter()
            .map(|t| MockTrait::new(structname, &generics, t, &vis))
            .collect();

        MockItemStruct {
            attrs: mockable.attrs,
            generics,
            has_new,
            methods,
            modname,
            name: mockable.name,
            traits,
            vis
        }
    }
}

impl ToTokens for MockItemStruct {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let attrs = &self.attrs;
        let struct_name = &self.name;
        let (ig, tg, wc) = self.generics.split_for_impl();
        let modname = &self.modname;
        let calls = self.methods.0.iter()
            .map(|meth| meth.call(Some(modname)))
            .collect::<Vec<_>>();
        let contexts = self.methods.0.iter()
            .filter(|meth| meth.is_static())
            .map(|meth| meth.context_fn(Some(modname)))
            .collect::<Vec<_>>();
        let expects = self.methods.0.iter()
            .filter(|meth| !meth.is_static())
            .map(|meth| meth.expect(modname))
            .collect::<Vec<_>>();
        let method_checkpoints = self.methods.checkpoints();
        let new_method = self.new_method();
        let priv_mods = self.methods.priv_mods();
        let substructs = self.traits.iter()
            .map(|trait_| {
                MockItemTraitImpl {
                    attrs: trait_.attrs.clone(),
                    generics: self.generics.clone(),
                    fieldname: format_ident!("{}_expectations", trait_.name()),
                    methods: Methods(trait_.methods.clone()),
                    modname: format_ident!("{}_{}", &self.modname, trait_.name()),
                    name: format_ident!("{}_{}", &self.name, trait_.name()),
                }
            }).collect::<Vec<_>>();
        let substruct_expectations = self.traits.iter()
            .map(|trait_| format_ident!("{}_expectations", trait_.name()))
            .collect::<Vec<_>>();
        let mut field_definitions = substructs.iter()
            .map(|ss| {
                let fieldname = &ss.fieldname;
                let tyname = &ss.name;
                quote!(#fieldname: #tyname #tg)
            }).collect::<Vec<_>>();
        field_definitions.extend(self.methods.field_definitions(&modname));
        field_definitions.extend(self.phantom_fields());
        let mut default_inits = substructs.iter()
            .map(|ss| {
                let fieldname = &ss.fieldname;
                quote!(#fieldname: Default::default())
            }).collect::<Vec<_>>();
        default_inits.extend(self.methods.default_inits());
        default_inits.extend(self.phantom_default_inits());
        let trait_impls = self.traits.iter()
            .map(|ss| {
                let modname = format_ident!("{}_{}", &self.modname, ss.name());
                ss.trait_impl(&modname)
            }).collect::<Vec<_>>();
        let vis = &self.vis;
        quote!(
            #[allow(non_snake_case)]
            #[allow(missing_docs)]
            pub mod #modname {
                use super::*;
                #(#priv_mods)*
            }
            #[allow(non_camel_case_types)]
            #[allow(non_snake_case)]
            #[allow(missing_docs)]
            #(#attrs)*
            #vis struct #struct_name #ig #wc
            {
                #(#field_definitions),*
            }
            impl #ig ::std::default::Default for #struct_name #tg #wc {
                fn default() -> Self {
                    Self {
                        #(#default_inits),*
                    }
                }
            }
            #(#substructs)*
            impl #ig #struct_name #tg #wc {
                #(#calls)*
                #(#contexts)*
                #(#expects)*
                /// Validate that all current expectations for all methods have
                /// been satisfied, and discard them.
                pub fn checkpoint(&mut self) {
                    #(self.#substruct_expectations.checkpoint();)*
                    #(#method_checkpoints)*
                }
                #new_method
            }
            #(#trait_impls)*
        ).to_tokens(tokens);
    }
}

pub(crate) struct MockItemTraitImpl {
    attrs: Vec<Attribute>,
    generics: Generics,
    /// Inherent methods of the mock struct
    methods: Methods,
    /// Name of the overall module that holds all of the mock stuff
    // TODO: base this on name so we can get rid of MockableStruct.original_name
    modname: Ident,
    name: Ident,
    /// Name of the field of this type in the parent's structure
    fieldname: Ident,
}

impl MockItemTraitImpl {
    fn phantom_default_inits(&self) -> Vec<TokenStream> {
        phantom_default_inits(&self.generics)
    }

    fn phantom_fields(&self) -> Vec<TokenStream> {
        phantom_fields(&self.generics)
    }
}

impl ToTokens for MockItemTraitImpl {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let attrs_nodocs = format_attrs(&self.attrs, false);
        let struct_name = &self.name;
        let (ig, tg, wc) = self.generics.split_for_impl();
        let modname = &self.modname;
        let method_checkpoints = self.methods.checkpoints();
        let mut default_inits = self.methods.default_inits();
        default_inits.extend(self.phantom_default_inits());
        let mut field_definitions = self.methods.field_definitions(&modname);
        field_definitions.extend(self.phantom_fields());
        let priv_mods = self.methods.priv_mods();
        quote!(
            #[allow(non_snake_case)]
            #[allow(missing_docs)]
            pub mod #modname {
                use super::*;
                #(#priv_mods)*
            }
            #[allow(non_camel_case_types)]
            #[allow(non_snake_case)]
            #[allow(missing_docs)]
            #attrs_nodocs
            struct #struct_name #ig #wc
            {
                #(#field_definitions),*
            }
            impl #ig ::std::default::Default for #struct_name #tg #wc {
                fn default() -> Self {
                    Self {
                        #(#default_inits),*
                    }
                }
            }
            impl #ig #struct_name #tg #wc {
                /// Validate that all current expectations for all methods have
                /// been satisfied, and discard them.
                pub fn checkpoint(&mut self) {
                    #(#method_checkpoints)*
                }
            }
        ).to_tokens(tokens);
    }
}
<|endoftext|>


<commit_before>// vim: tw=80
use super::*;

use quote::ToTokens;

use crate::{
    mock_function::MockFunction,
    mock_trait::MockTrait
};

pub(crate) struct MockItemStruct {
    attrs: Vec<Attribute>,
    generics: Generics,
    /// Inherent methods of the mock struct
    methods: Vec<MockFunction>,
    /// Name of the overall module that holds all of the mock stuff
    // TODO: base this on name so we can get rid of MockableStruct.original_name
    modname: Ident,
    name: Ident,
    /// Is this a whole MockStruct or just a substructure for a trait impl?
    substruct: bool,
    traits: Vec<MockTrait>,
    vis: Visibility,
}

impl MockItemStruct {
    fn new_method(&self) -> impl ToTokens {
        // TODO: don't add the new method if the struct already has one
        if self.substruct {
            TokenStream::new()
        } else {
            quote!(
                /// Create a new mock object with no expectations.
                ///
                /// This method will not be generated if the real struct
                /// already has a `new` method.  However, it *will* be
                /// generated if the struct implements a trait with a `new`
                /// method.  The trait's `new` method can still be called
                /// like `<MockX as TraitY>::new`
                pub fn new() -> Self {
                    Self::default()
                }
            )
        }
    }
}

impl From<MockableStruct> for MockItemStruct {
    fn from(mockable: MockableStruct) -> MockItemStruct {
        let mock_ident = gen_mod_ident(&mockable.name, None);
        let modname = gen_mod_ident(&mockable.original_name, None);
        let struct_name = &mockable.name;
        let vis = &mockable.vis;
        let methods = mockable.methods.into_iter()
            .map(|meth|
                mock_function::Builder::new(&meth.sig, &meth.vis)
                    .attrs(&meth.attrs)
                    .parent(&mock_ident)
                    .struct_(&struct_name)
                    .levels(1)
                    .call_levels(0)
                    .build()
            ).collect::<Vec<_>>();
        let structname = &mockable.name;
        let traits = mockable.traits.into_iter()
            .map(|t| MockTrait::new(structname, t))
            .collect();
        MockItemStruct {
            attrs: mockable.attrs,
            generics: mockable.generics,
            methods,
            modname,
            name: mockable.name,
            substruct: false,
            traits,
            vis: mockable.vis
        }
    }
}

impl ToTokens for MockItemStruct {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let attrs = &self.attrs;
        let struct_name = &self.name;
        let (ig, tg, wc) = self.generics.split_for_impl(); //TODO
        let modname = &self.modname;
        let calls = if self.substruct {
            Vec::new()
        } else {
            self.methods.iter()
                .map(|meth| meth.call())
                .collect::<Vec<_>>()
        };
        let method_checkpoints = self.methods.iter()
            .map(|meth| meth.checkpoint())
            .collect::<Vec<_>>();
        let default_constructions = self.methods.iter()
            .map(|meth| {
                let name = meth.name();
                let attrs = meth.format_attrs(false);
                let expectations_obj = &meth.expectations_obj();
                quote!(#attrs #name: #modname::#expectations_obj::default(),)
            }).collect::<Vec<_>>();
        let expectations_objects = self.methods.iter()
            .map(|meth| {
                let name = meth.name();
                let attrs = meth.format_attrs(false);
                let expectations_obj = &meth.expectations_obj();
                quote!(#attrs #name: #modname::#expectations_obj,)
            }).collect::<Vec<_>>();
        let expects = if self.substruct {
            Vec::new()
        } else {
            self.methods.iter()
                .map(|meth| meth.expect(&modname))
                .collect::<Vec<_>>()
        };
        let new_method = self.new_method();
        let priv_mods = self.methods.iter()
            .map(|meth| meth.priv_module())
            .collect::<Vec<_>>();
        let substructs = self.traits.iter()
            .map(|trait_| {
                MockItemStruct {
                    attrs: trait_.attrs.clone(),
                    generics: trait_.generics.clone(),
                    methods: trait_.methods.clone(),
                    modname: format_ident!("{}_{}", &self.modname, trait_.name()),
                    name: format_ident!("{}_{}", &self.name, trait_.name()),
                    substruct: true,
                    traits: Vec::new(),
                    vis: Visibility::Inherited,
                }
            }).collect::<Vec<_>>();
        let substruct_expectations = self.traits.iter()
            .map(|trait_| format_ident!("{}_expectations", trait_.name()))
            .collect::<Vec<_>>();
        let substruct_type_names = substructs.iter()
            .map(|ss| ss.name.clone())
            .collect::<Vec<_>>();
        let trait_impls = self.traits.iter()
            .map(|ss| ss.trait_impl(&self.modname))
            .collect::<Vec<_>>();
        let vis = &self.vis;
        quote!(
            #[allow(non_snake_case)]
            #[doc(hidden)]
            pub mod #modname {
                use super::*;
                #(#priv_mods)*
            }
            #[allow(non_camel_case_types)]
            #[allow(non_snake_case)]
            #[allow(missing_docs)]
            #(#attrs)*
            #vis struct #struct_name #ig #wc
            {
                #(#expectations_objects)*
                #(#substruct_expectations: #substruct_type_names)*
            }
            impl #ig ::std::default::Default for #struct_name #tg #wc {
                fn default() -> Self {
                    Self {
                        // TODO: try removing the type names, and just use
                        // "default"
                        #(#substruct_expectations: #substruct_type_names::default())*
                        #(#default_constructions)*
                    }
                }
            }
            #(#substructs)*
            impl #ig #struct_name #tg #wc {
                #(#calls #expects)*
                /// Validate that all current expectations for all methods have
                /// been satisfied, and discard them.
                pub fn checkpoint(&mut self) {
                    #(self.#substruct_expectations.checkpoint();)*
                    #(#method_checkpoints)*
                }
                #new_method
            }
            #(#trait_impls)*
        ).to_tokens(tokens);
    }
}
<commit_msg>Fix mocking a struct with multiple traits<commit_after>// vim: tw=80
use super::*;

use quote::ToTokens;

use crate::{
    mock_function::MockFunction,
    mock_trait::MockTrait
};

pub(crate) struct MockItemStruct {
    attrs: Vec<Attribute>,
    generics: Generics,
    /// Inherent methods of the mock struct
    methods: Vec<MockFunction>,
    /// Name of the overall module that holds all of the mock stuff
    // TODO: base this on name so we can get rid of MockableStruct.original_name
    modname: Ident,
    name: Ident,
    /// Is this a whole MockStruct or just a substructure for a trait impl?
    substruct: bool,
    traits: Vec<MockTrait>,
    vis: Visibility,
}

impl MockItemStruct {
    fn new_method(&self) -> impl ToTokens {
        // TODO: don't add the new method if the struct already has one
        if self.substruct {
            TokenStream::new()
        } else {
            quote!(
                /// Create a new mock object with no expectations.
                ///
                /// This method will not be generated if the real struct
                /// already has a `new` method.  However, it *will* be
                /// generated if the struct implements a trait with a `new`
                /// method.  The trait's `new` method can still be called
                /// like `<MockX as TraitY>::new`
                pub fn new() -> Self {
                    Self::default()
                }
            )
        }
    }
}

impl From<MockableStruct> for MockItemStruct {
    fn from(mockable: MockableStruct) -> MockItemStruct {
        let mock_ident = gen_mod_ident(&mockable.name, None);
        let modname = gen_mod_ident(&mockable.original_name, None);
        let struct_name = &mockable.name;
        let vis = &mockable.vis;
        let methods = mockable.methods.into_iter()
            .map(|meth|
                mock_function::Builder::new(&meth.sig, &meth.vis)
                    .attrs(&meth.attrs)
                    .parent(&mock_ident)
                    .struct_(&struct_name)
                    .levels(1)
                    .call_levels(0)
                    .build()
            ).collect::<Vec<_>>();
        let structname = &mockable.name;
        let traits = mockable.traits.into_iter()
            .map(|t| MockTrait::new(structname, t))
            .collect();
        MockItemStruct {
            attrs: mockable.attrs,
            generics: mockable.generics,
            methods,
            modname,
            name: mockable.name,
            substruct: false,
            traits,
            vis: mockable.vis
        }
    }
}

impl ToTokens for MockItemStruct {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let attrs = &self.attrs;
        let struct_name = &self.name;
        let (ig, tg, wc) = self.generics.split_for_impl(); //TODO
        let modname = &self.modname;
        let calls = if self.substruct {
            Vec::new()
        } else {
            self.methods.iter()
                .map(|meth| meth.call())
                .collect::<Vec<_>>()
        };
        let method_checkpoints = self.methods.iter()
            .map(|meth| meth.checkpoint())
            .collect::<Vec<_>>();
        let default_constructions = self.methods.iter()
            .map(|meth| {
                let name = meth.name();
                let attrs = meth.format_attrs(false);
                let expectations_obj = &meth.expectations_obj();
                quote!(#attrs #name: #modname::#expectations_obj::default(),)
            }).collect::<Vec<_>>();
        let expectations_objects = self.methods.iter()
            .map(|meth| {
                let name = meth.name();
                let attrs = meth.format_attrs(false);
                let expectations_obj = &meth.expectations_obj();
                quote!(#attrs #name: #modname::#expectations_obj)
            }).collect::<Vec<_>>();
        let expects = if self.substruct {
            Vec::new()
        } else {
            self.methods.iter()
                .map(|meth| meth.expect(&modname))
                .collect::<Vec<_>>()
        };
        let new_method = self.new_method();
        let priv_mods = self.methods.iter()
            .map(|meth| meth.priv_module())
            .collect::<Vec<_>>();
        let substructs = self.traits.iter()
            .map(|trait_| {
                MockItemStruct {
                    attrs: trait_.attrs.clone(),
                    generics: trait_.generics.clone(),
                    methods: trait_.methods.clone(),
                    modname: format_ident!("{}_{}", &self.modname, trait_.name()),
                    name: format_ident!("{}_{}", &self.name, trait_.name()),
                    substruct: true,
                    traits: Vec::new(),
                    vis: Visibility::Inherited,
                }
            }).collect::<Vec<_>>();
        let substruct_expectations = self.traits.iter()
            .map(|trait_| format_ident!("{}_expectations", trait_.name()))
            .collect::<Vec<_>>();
        let substruct_type_names = substructs.iter()
            .map(|ss| ss.name.clone())
            .collect::<Vec<_>>();
        let trait_impls = self.traits.iter()
            .map(|ss| ss.trait_impl(&self.modname))
            .collect::<Vec<_>>();
        let vis = &self.vis;
        quote!(
            #[allow(non_snake_case)]
            #[doc(hidden)]
            pub mod #modname {
                use super::*;
                #(#priv_mods)*
            }
            #[allow(non_camel_case_types)]
            #[allow(non_snake_case)]
            #[allow(missing_docs)]
            #(#attrs)*
            #vis struct #struct_name #ig #wc
            {
                #(#expectations_objects),*
                #(#substruct_expectations: #substruct_type_names),*
            }
            impl #ig ::std::default::Default for #struct_name #tg #wc {
                fn default() -> Self {
                    Self {
                        // TODO: try removing the type names, and just use
                        // "default"
                        #(#substruct_expectations: #substruct_type_names::default()),*
                        #(#default_constructions)*
                    }
                }
            }
            #(#substructs)*
            impl #ig #struct_name #tg #wc {
                #(#calls #expects)*
                /// Validate that all current expectations for all methods have
                /// been satisfied, and discard them.
                pub fn checkpoint(&mut self) {
                    #(self.#substruct_expectations.checkpoint();)*
                    #(#method_checkpoints)*
                }
                #new_method
            }
            #(#trait_impls)*
        ).to_tokens(tokens);
    }
}
<|endoftext|>


<commit_before>#![deny(clippy::all, clippy::pedantic)]

use std::fmt::Display;
use thiserror::Error;

fn assert<T: Display>(expected: &str, value: T) {
    assert_eq!(expected, value.to_string());
}

#[test]
fn test_braced() {
    #[derive(Error, Debug)]
    #[error("braced error: {msg}")]
    struct Error {
        msg: String,
    }

    let msg = "T".to_owned();
    assert("braced error: T", Error { msg });
}

#[test]
fn test_braced_unused() {
    #[derive(Error, Debug)]
    #[error("braced error")]
    struct Error {
        extra: usize,
    }

    assert("braced error", Error { extra: 0 });
}

#[test]
fn test_tuple() {
    #[derive(Error, Debug)]
    #[error("tuple error: {0}")]
    struct Error(usize);

    assert("tuple error: 0", Error(0));
}

#[test]
fn test_unit() {
    #[derive(Error, Debug)]
    #[error("unit error")]
    struct Error;

    assert("unit error", Error);
}

#[test]
fn test_enum() {
    #[derive(Error, Debug)]
    enum Error {
        #[error("braced error: {id}")]
        Braced { id: usize },
        #[error("tuple error: {0}")]
        Tuple(usize),
        #[error("unit error")]
        Unit,
    }

    assert("braced error: 0", Error::Braced { id: 0 });
    assert("tuple error: 0", Error::Tuple(0));
    assert("unit error", Error::Unit);
}

#[test]
fn test_constants() {
    #[derive(Error, Debug)]
    #[error("{MSG}: {id:?} (code {CODE:?})")]
    struct Error {
        id: &'static str,
    }

    const MSG: &str = "failed to do";
    const CODE: usize = 9;

    assert("failed to do: \"\" (code 9)", Error { id: "" });
}

#[test]
fn test_inherit() {
    #[derive(Error, Debug)]
    #[error("{0}")]
    enum Error {
        Some(&'static str),
        #[error("other error")]
        Other(&'static str),
    }

    assert("some error", Error::Some("some error"));
    assert("other error", Error::Other("..."));
}

#[test]
fn test_brace_escape() {
    #[derive(Error, Debug)]
    #[error("fn main() {{}}")]
    struct Error;

    assert("fn main() {}", Error);
}

#[test]
fn test_expr() {
    #[derive(Error, Debug)]
    #[error("1 + 1 = {}", 1 + 1)]
    struct Error;
    assert("1 + 1 = 2", Error);
}

#[test]
fn test_nested() {
    #[derive(Error, Debug)]
    #[error("!bool = {}", not(.0))]
    struct Error(bool);

    #[allow(clippy::trivially_copy_pass_by_ref)]
    fn not(bool: &bool) -> bool {
        !*bool
    }

    assert("!bool = false", Error(true));
}

#[test]
fn test_match() {
    #[derive(Error, Debug)]
    #[error("{}: {0}", match .1 {
        Some(n) => format!("error occurred with {}", n),
        None => "there was an empty error".to_owned(),
    })]
    struct Error(String, Option<usize>);

    assert(
        "error occurred with 1: ...",
        Error("...".to_owned(), Some(1)),
    );
    assert(
        "there was an empty error: ...",
        Error("...".to_owned(), None),
    );
}

#[test]
fn test_void() {
    #[allow(clippy::empty_enum)]
    #[derive(Error, Debug)]
    #[error("...")]
    pub enum Error {}

    let _: Error;
}

#[test]
fn test_mixed() {
    #[derive(Error, Debug)]
    #[error("a={a} :: b={} :: c={c} :: d={d}", 1, c = 2, d = 3)]
    struct Error {
        a: usize,
        d: usize,
    }

    assert("a=0 :: b=1 :: c=2 :: d=3", Error { a: 0, d: 0 });
}

#[test]
fn test_ints() {
    #[derive(Error, Debug)]
    enum Error {
        #[error("error {0}")]
        Tuple(usize, usize),
        #[error("error {0}", '?')]
        Struct { v: usize },
    }

    assert("error 9", Error::Tuple(9, 0));
    assert("error ?", Error::Struct { v: 0 });
}

#[test]
fn test_trailing_comma() {
    #[derive(Error, Debug)]
    #[error(
        "error {0}",
    )]
    #[rustfmt::skip]
    struct Error(char);

    assert("error ?", Error('?'));
}

#[test]
fn test_field() {
    #[derive(Debug)]
    struct Inner {
        data: usize,
    }

    #[derive(Error, Debug)]
    #[error("{}", .0.data)]
    struct Error(Inner);

    assert("0", Error(Inner { data: 0 }));
}

#[test]
fn test_macro_rules() {
    // Regression test for https://github.com/dtolnay/thiserror/issues/86

    macro_rules! decl_error {
        ($variant:ident($value:ident)) => {
            #[derive(Debug, Error)]
            pub enum Error0 {
                #[error("{0:?}")]
                $variant($value),
            }

            #[derive(Debug, Error)]
            #[error("{0:?}")]
            pub enum Error1 {
                $variant($value),
            }
        };
    }

    decl_error!(Repro(u8));

    assert("0", Error0::Repro(0));
    assert("0", Error1::Repro(0));
}

#[test]
fn test_raw() {
    #[derive(Error, Debug)]
    #[error("braced raw error: {r#fn}")]
    struct Error {
        r#fn: String,
    }

    assert(
        "braced raw error: T",
        Error {
            r#fn: "T".to_owned(),
        },
    );
}

#[test]
fn test_raw_enum() {
    #[derive(Error, Debug)]
    enum Error {
        #[error("braced raw error: {r#fn}")]
        Braced { r#fn: String },
    }

    assert(
        "braced raw error: T",
        Error::Braced {
            r#fn: "T".to_owned(),
        },
    );
}

#[test]
fn test_raw_conflict() {
    #[derive(Error, Debug)]
    enum Error {
        #[error("braced raw error: {r#func}, {func}", func = "U")]
        Braced { r#func: String },
    }

    assert(
        "braced raw error: T, U",
        Error::Braced {
            r#func: "T".to_owned(),
        },
    );
}
<commit_msg>Condense raw identifier unit tests<commit_after>#![deny(clippy::all, clippy::pedantic)]

use std::fmt::Display;
use thiserror::Error;

fn assert<T: Display>(expected: &str, value: T) {
    assert_eq!(expected, value.to_string());
}

#[test]
fn test_braced() {
    #[derive(Error, Debug)]
    #[error("braced error: {msg}")]
    struct Error {
        msg: String,
    }

    let msg = "T".to_owned();
    assert("braced error: T", Error { msg });
}

#[test]
fn test_braced_unused() {
    #[derive(Error, Debug)]
    #[error("braced error")]
    struct Error {
        extra: usize,
    }

    assert("braced error", Error { extra: 0 });
}

#[test]
fn test_tuple() {
    #[derive(Error, Debug)]
    #[error("tuple error: {0}")]
    struct Error(usize);

    assert("tuple error: 0", Error(0));
}

#[test]
fn test_unit() {
    #[derive(Error, Debug)]
    #[error("unit error")]
    struct Error;

    assert("unit error", Error);
}

#[test]
fn test_enum() {
    #[derive(Error, Debug)]
    enum Error {
        #[error("braced error: {id}")]
        Braced { id: usize },
        #[error("tuple error: {0}")]
        Tuple(usize),
        #[error("unit error")]
        Unit,
    }

    assert("braced error: 0", Error::Braced { id: 0 });
    assert("tuple error: 0", Error::Tuple(0));
    assert("unit error", Error::Unit);
}

#[test]
fn test_constants() {
    #[derive(Error, Debug)]
    #[error("{MSG}: {id:?} (code {CODE:?})")]
    struct Error {
        id: &'static str,
    }

    const MSG: &str = "failed to do";
    const CODE: usize = 9;

    assert("failed to do: \"\" (code 9)", Error { id: "" });
}

#[test]
fn test_inherit() {
    #[derive(Error, Debug)]
    #[error("{0}")]
    enum Error {
        Some(&'static str),
        #[error("other error")]
        Other(&'static str),
    }

    assert("some error", Error::Some("some error"));
    assert("other error", Error::Other("..."));
}

#[test]
fn test_brace_escape() {
    #[derive(Error, Debug)]
    #[error("fn main() {{}}")]
    struct Error;

    assert("fn main() {}", Error);
}

#[test]
fn test_expr() {
    #[derive(Error, Debug)]
    #[error("1 + 1 = {}", 1 + 1)]
    struct Error;
    assert("1 + 1 = 2", Error);
}

#[test]
fn test_nested() {
    #[derive(Error, Debug)]
    #[error("!bool = {}", not(.0))]
    struct Error(bool);

    #[allow(clippy::trivially_copy_pass_by_ref)]
    fn not(bool: &bool) -> bool {
        !*bool
    }

    assert("!bool = false", Error(true));
}

#[test]
fn test_match() {
    #[derive(Error, Debug)]
    #[error("{}: {0}", match .1 {
        Some(n) => format!("error occurred with {}", n),
        None => "there was an empty error".to_owned(),
    })]
    struct Error(String, Option<usize>);

    assert(
        "error occurred with 1: ...",
        Error("...".to_owned(), Some(1)),
    );
    assert(
        "there was an empty error: ...",
        Error("...".to_owned(), None),
    );
}

#[test]
fn test_void() {
    #[allow(clippy::empty_enum)]
    #[derive(Error, Debug)]
    #[error("...")]
    pub enum Error {}

    let _: Error;
}

#[test]
fn test_mixed() {
    #[derive(Error, Debug)]
    #[error("a={a} :: b={} :: c={c} :: d={d}", 1, c = 2, d = 3)]
    struct Error {
        a: usize,
        d: usize,
    }

    assert("a=0 :: b=1 :: c=2 :: d=3", Error { a: 0, d: 0 });
}

#[test]
fn test_ints() {
    #[derive(Error, Debug)]
    enum Error {
        #[error("error {0}")]
        Tuple(usize, usize),
        #[error("error {0}", '?')]
        Struct { v: usize },
    }

    assert("error 9", Error::Tuple(9, 0));
    assert("error ?", Error::Struct { v: 0 });
}

#[test]
fn test_trailing_comma() {
    #[derive(Error, Debug)]
    #[error(
        "error {0}",
    )]
    #[rustfmt::skip]
    struct Error(char);

    assert("error ?", Error('?'));
}

#[test]
fn test_field() {
    #[derive(Debug)]
    struct Inner {
        data: usize,
    }

    #[derive(Error, Debug)]
    #[error("{}", .0.data)]
    struct Error(Inner);

    assert("0", Error(Inner { data: 0 }));
}

#[test]
fn test_macro_rules() {
    // Regression test for https://github.com/dtolnay/thiserror/issues/86

    macro_rules! decl_error {
        ($variant:ident($value:ident)) => {
            #[derive(Debug, Error)]
            pub enum Error0 {
                #[error("{0:?}")]
                $variant($value),
            }

            #[derive(Debug, Error)]
            #[error("{0:?}")]
            pub enum Error1 {
                $variant($value),
            }
        };
    }

    decl_error!(Repro(u8));

    assert("0", Error0::Repro(0));
    assert("0", Error1::Repro(0));
}

#[test]
fn test_raw() {
    #[derive(Error, Debug)]
    #[error("braced raw error: {r#fn}")]
    struct Error {
        r#fn: &'static str,
    }

    assert("braced raw error: T", Error { r#fn: "T" });
}

#[test]
fn test_raw_enum() {
    #[derive(Error, Debug)]
    enum Error {
        #[error("braced raw error: {r#fn}")]
        Braced { r#fn: &'static str },
    }

    assert("braced raw error: T", Error::Braced { r#fn: "T" });
}

#[test]
fn test_raw_conflict() {
    #[derive(Error, Debug)]
    enum Error {
        #[error("braced raw error: {r#func}, {func}", func = "U")]
        Braced { r#func: &'static str },
    }

    assert("braced raw error: T, U", Error::Braced { r#func: "T" });
}
<|endoftext|>


<commit_before>use postgres;
use uuid::Uuid;
use error::Error;
use chrono::{NaiveDateTime, UTC};
use super::{conn, Model};
use model::enclosure::Enclosure;
use scraper;
use Track;
use Playlist;
use Album;
use model::PaginatedCollection;
use serde_json::Value;
use feed_rs;

static PROPS: [&'static str; 20]  = ["id",
                                     "url",
                                     "title",
                                     "description",
                                     "visual_url",
                                     "locale",

                                     "summary",
                                     "content",
                                     "author",
                                     "crawled",
                                     "published",
                                     "updated",
                                     "fingerprint",
                                     "origin_id",
                                     "alternate",
                                     "keywords",
                                     "enclosure",

                                     "feed_id",

                                     "created_at",
                                     "updated_at"];

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Entry {
    pub id:          Uuid,
    pub url:         String,
    pub title:       Option<String>,
    pub description: Option<String>,
    pub visual_url:  Option<String>,
    pub locale:      Option<String>,
    pub summary:     Option<String>,
    pub content:     Option<String>,
    pub author:      Option<String>,
    pub crawled:     NaiveDateTime,
    pub published:   NaiveDateTime,
    pub updated:     Option<NaiveDateTime>,
    pub fingerprint: String,
    pub origin_id:   String,
    pub alternate:   Value,
    pub keywords:    Value,
    pub enclosure:   Value,
    pub feed_id:     Option<Uuid>,
    pub created_at:  NaiveDateTime,
    pub updated_at:  NaiveDateTime,
    pub tracks:      Vec<Track>,
    pub playlists:   Vec<Playlist>,
    pub albums:      Vec<Album>,
}

impl<'a> Model<'a> for Entry {
    fn table_name() -> String {
        "entries".to_string()
    }
    fn props_str(prefix: &str) -> String {
        PROPS
            .iter()
            .map(|&p| format!("{}{}", prefix, p))
            .collect::<Vec<String>>().join(",")
    }
    fn rows_to_items(rows: postgres::rows::Rows) -> Vec<Entry> {
        let mut entries = Vec::new();
        for row in rows.iter() {
            entries.push(Entry {
                id:          row.get(0),
                url:         row.get(1),
                title:       row.get(2),
                description: row.get(3),
                visual_url:  row.get(4),
                locale:      row.get(5),
                summary:     row.get(6),
                content:     row.get(7),
                author:      row.get(8),
                crawled:     row.get(9),
                published:   row.get(10),
                updated:     row.get(11),
                fingerprint: row.get(12),
                origin_id:   row.get(13),
                alternate:   row.get(14),
                keywords:    row.get(15),
                enclosure:   row.get(16),
                feed_id:     row.get(17),
                created_at:  row.get(18),
                updated_at:  row.get(19),
                tracks:      Track::find_by_entry_id(row.get(0)),
                playlists:   Playlist::find_by_entry_id(row.get(0)),
                albums:      Album::find_by_entry_id(row.get(0)),
            })
        }
        entries
    }
    fn create(&self) -> Result<Entry, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO entries (url, published) VALUES ($1, $2) RETURNING id"));
        let rows = try!(stmt.query(&[&self.url, &NaiveDateTime::from_timestamp(0, 0)]));
        let mut entry = self.clone();
        for row in rows.iter() {
            entry.id = row.get(0);
        }
        Ok(entry)
    }
    fn save(&mut self) -> Result<(), Error> {
        self.updated_at = UTC::now().naive_utc();
        let conn = try!(conn());
        let stmt = try!(conn.prepare("UPDATE entries SET
                                   url         = $2,
                                   title       = $3,
                                   description = $4,
                                   visual_url  = $5,
                                   locale      = $6,
                                   summary     = $7,
                                   content     = $8,
                                   author      = $9,
                                   crawled     = $10,
                                   published   = $11,
                                   updated     = $12,
                                   fingerprint = $13,
                                   origin_id   = $14,
                                   alternate   = $15,
                                   keywords    = $16,
                                   enclosure   = $17,
                                   feed_id     = $18,
                                   created_at  = $19,
                                   updated_at  = $20
                                 WHERE id = $1"));
        let result = stmt.query(&[&self.id,
                                  &self.url,
                                  &self.title,
                                  &self.description,
                                  &self.visual_url,
                                  &self.locale,
                                  &self.summary,
                                  &self.content,
                                  &self.author,
                                  &self.crawled,
                                  &self.published,
                                  &self.updated,
                                  &self.fingerprint,
                                  &self.origin_id,
                                  &self.alternate,
                                  &self.keywords,
                                  &self.enclosure,
                                  &self.feed_id,
                                  &self.created_at,
                                  &self.updated_at]);
        try!(result);
        Ok(())
    }
}

impl Entry {
    pub fn new(url: String) -> Result<Entry, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO entries (url) VALUES ($1) RETURNING id"));
        let rows = try!(stmt.query(&[&url]));
        for row in rows.iter() {
            let entry = Entry {
                id:          row.get(0),
                url:         url,
                title:       None,
                description: None,
                visual_url:  None,
                locale:      None,

                summary:     None,
                content:     None,
                author:      None,
                crawled:     NaiveDateTime::from_timestamp(0, 0),
                published:   NaiveDateTime::from_timestamp(0, 0), // exclude from api response
                updated:     None,
                fingerprint: "".to_string(),
                origin_id:   "".to_string(),
                alternate:   Value::Null,
                keywords:    Value::Null,
                enclosure:   Value::Null,
                feed_id:     None,

                created_at:  UTC::now().naive_utc(),
                updated_at:  UTC::now().naive_utc(),

                tracks:      Vec::new(),
                playlists:   Vec::new(),
                albums:      Vec::new(),
            };
            return Ok(entry);
        }
        Err(Error::Unexpected)
    }

    pub fn find_by_url(url: &str) -> Result<Entry, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare(
            &format!("SELECT {} FROM entries
                        WHERE url = $1", Self::props_str(""))));
        let rows = try!(stmt.query(&[&url]));
        let entries = Entry::rows_to_items(rows);
        if entries.len() > 0 {
            return Ok(entries[0].clone());
        }
        return Err(Error::NotFound)
    }

    pub fn find_or_create_by_url(url: String) -> Result<Entry, Error> {
        match Entry::find_by_url(&url) {
            Ok(entry) => Ok(entry),
            Err(Error::NotFound) => Entry::create_by_url(url),
            Err(e) => Err(e),
        }
    }

    pub fn find_by_feed_id(feed_id: Uuid, newer_than: Option<NaiveDateTime>, page: i64, per_page: i64) -> PaginatedCollection<Entry> {
        let conn = conn().unwrap();
        let stmt = conn.prepare(
            &format!("SELECT {} FROM entries
                        WHERE entries.feed_id = $1 AND entries.published >= $2
                        ORDER BY entries.published DESC
                        LIMIT $4 OFFSET $3",
                     Entry::props_str(""))).unwrap();
        let offset = page * per_page;
        let published = newer_than.unwrap_or(NaiveDateTime::from_timestamp(1000, 0)); // ignore 0 timestamp
        let rows   = stmt.query(&[&feed_id, &published, &offset, &per_page]).unwrap();
        let items  = Self::rows_to_items(rows);
        let mut total: i64 = 0;
        let sql = "SELECT COUNT(*) FROM entries WHERE entries.feed_id = $1";
        for row in conn.query(&sql, &[&feed_id]).unwrap().iter() {
            total = row.get(0);
        }
        PaginatedCollection {
            page:     page,
            per_page: per_page,
            total:    total,
            items:    items,
        }
    }

    pub fn create_by_url(url: String) -> Result<Entry, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO entries (url, published) VALUES ($1, $2) RETURNING id"));
        let rows = try!(stmt.query(&[&url, &NaiveDateTime::from_timestamp(0, 0)]));
        for row in rows.iter() {
            let entry = Entry {
                id:          row.get(0),
                url:         url,
                title:       None,
                description: None,
                visual_url:  None,
                locale:      None,

                summary:     None,
                content:     None,
                author:      None,
                crawled:     UTC::now().naive_utc(),
                published:   UTC::now().naive_utc(),
                updated:     None,
                fingerprint: "".to_string(),
                origin_id:   "".to_string(),
                alternate:   Value::Null,
                keywords:    Value::Null,
                enclosure:   Value::Null,
                feed_id:     None,

                created_at:  UTC::now().naive_utc(),
                updated_at:  UTC::now().naive_utc(),

                tracks:      Vec::new(),
                playlists:   Vec::new(),
                albums:      Vec::new(),
            };
            return Ok(entry);
        }
        Err(Error::Unexpected)
    }

    pub fn is_valid(&self) -> bool {
        self.published.timestamp() >= 1000
    }

    pub fn find_or_create_by_url_if_invalid(url: String) -> Result<Entry, Error> {
        match Entry::find_by_url(&url) {
            Ok(entry) => {
                if !entry.is_valid() {
                    Ok(entry)
                } else {
                    Err(Error::NotFound)
                }
            },
            Err(_) => {
                Entry::create_by_url(url)
            }
        }
    }

    pub fn update_with_feed_entry(&mut self, entry: &feed_rs::Entry) {
        self.title       = entry.title.clone();
        self.summary     = entry.summary.clone();
        self.content     = entry.content.clone();
        self.author      = entry.author.clone();
        self.crawled     = UTC::now().naive_utc();
        self.published   = entry.published;
        self.updated     = entry.updated;
        self.fingerprint = entry.fingerprint.clone();
        self.origin_id   = entry.id.clone();
        self.alternate   = json!(entry.alternate);
        self.keywords    = json!(entry.keywords);
        self.enclosure   = json!(entry.enclosure);

        self.updated_at  = UTC::now().naive_utc();
    }

    pub fn playlistify(&mut self) -> Result<(), Error> {
        let product = try!(scraper::extract(&self.url));
        match product.og_obj {
            Some(og_obj) => {
                self.title       = Some(og_obj.title);
                self.description = og_obj.description;
                self.locale      = og_obj.locale;
                self.visual_url  = og_obj.images.first().map(|i| i.url.clone());
            },
            None => (),
        }
        for t in product.tracks {
            let new_track = try!(Track::find_or_create(t.provider, t.identifier.to_string()));
            let mut track = t.clone();
            track.id      = new_track.id;
            try!(track.fetch_detail().save());
            match self.tracks.iter().find(|&t| t.id == track.id) {
                Some(_) => (),
                None    => try!(self.add_track(track.clone())),
            }
        }
        for p in product.playlists {
            let new_playlist = try!(Playlist::find_or_create(p.provider, p.identifier.to_string()));
            let mut playlist = p.clone();
            playlist.id      = new_playlist.id;
            try!(playlist.save());
            match self.playlists.iter().find(|&p| p.id == playlist.id) {
                Some(_) => (),
                None    => try!(self.add_playlist(playlist.clone())),
            }
        }
        for a in product.albums {
            let new_album = try!(Album::find_or_create(a.provider, a.identifier.to_string()));
            let mut album = a.clone();
            album.id      = new_album.id;
            try!(album.save());
            match self.albums.iter().find(|&a| a.id == album.id) {
                Some(_) => (),
                None    => try!(self.add_album(album.clone())),
            }
        }

        Ok(())
    }

    pub fn add_track(&mut self, track: Track) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO track_entries (track_id, entry_id)
                                 VALUES ($1, $2)"));
        try!(stmt.query(&[&track.id, &self.id]));
        self.tracks.push(track);
        Ok(())
    }

    pub fn add_playlist(&mut self, playlist: Playlist) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO playlist_entries (playlist_id, entry_id)
                                 VALUES ($1, $2)"));
        try!(stmt.query(&[&playlist.id, &self.id]));
        self.playlists.push(playlist);
        Ok(())
    }

    pub fn add_album(&mut self, album: Album) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO album_entries (album_id, entry_id)
                                 VALUES ($1, $2)"));
        try!(stmt.query(&[&album.id, &self.id]));
        self.albums.push(album);
        Ok(())
    }
}
<commit_msg>Don't override Entry#title by opengraph result if the result title is empty<commit_after>use postgres;
use uuid::Uuid;
use error::Error;
use chrono::{NaiveDateTime, UTC};
use super::{conn, Model};
use model::enclosure::Enclosure;
use scraper;
use Track;
use Playlist;
use Album;
use model::PaginatedCollection;
use serde_json::Value;
use feed_rs;

static PROPS: [&'static str; 20]  = ["id",
                                     "url",
                                     "title",
                                     "description",
                                     "visual_url",
                                     "locale",

                                     "summary",
                                     "content",
                                     "author",
                                     "crawled",
                                     "published",
                                     "updated",
                                     "fingerprint",
                                     "origin_id",
                                     "alternate",
                                     "keywords",
                                     "enclosure",

                                     "feed_id",

                                     "created_at",
                                     "updated_at"];

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Entry {
    pub id:          Uuid,
    pub url:         String,
    pub title:       Option<String>,
    pub description: Option<String>,
    pub visual_url:  Option<String>,
    pub locale:      Option<String>,
    pub summary:     Option<String>,
    pub content:     Option<String>,
    pub author:      Option<String>,
    pub crawled:     NaiveDateTime,
    pub published:   NaiveDateTime,
    pub updated:     Option<NaiveDateTime>,
    pub fingerprint: String,
    pub origin_id:   String,
    pub alternate:   Value,
    pub keywords:    Value,
    pub enclosure:   Value,
    pub feed_id:     Option<Uuid>,
    pub created_at:  NaiveDateTime,
    pub updated_at:  NaiveDateTime,
    pub tracks:      Vec<Track>,
    pub playlists:   Vec<Playlist>,
    pub albums:      Vec<Album>,
}

impl<'a> Model<'a> for Entry {
    fn table_name() -> String {
        "entries".to_string()
    }
    fn props_str(prefix: &str) -> String {
        PROPS
            .iter()
            .map(|&p| format!("{}{}", prefix, p))
            .collect::<Vec<String>>().join(",")
    }
    fn rows_to_items(rows: postgres::rows::Rows) -> Vec<Entry> {
        let mut entries = Vec::new();
        for row in rows.iter() {
            entries.push(Entry {
                id:          row.get(0),
                url:         row.get(1),
                title:       row.get(2),
                description: row.get(3),
                visual_url:  row.get(4),
                locale:      row.get(5),
                summary:     row.get(6),
                content:     row.get(7),
                author:      row.get(8),
                crawled:     row.get(9),
                published:   row.get(10),
                updated:     row.get(11),
                fingerprint: row.get(12),
                origin_id:   row.get(13),
                alternate:   row.get(14),
                keywords:    row.get(15),
                enclosure:   row.get(16),
                feed_id:     row.get(17),
                created_at:  row.get(18),
                updated_at:  row.get(19),
                tracks:      Track::find_by_entry_id(row.get(0)),
                playlists:   Playlist::find_by_entry_id(row.get(0)),
                albums:      Album::find_by_entry_id(row.get(0)),
            })
        }
        entries
    }
    fn create(&self) -> Result<Entry, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO entries (url, published) VALUES ($1, $2) RETURNING id"));
        let rows = try!(stmt.query(&[&self.url, &NaiveDateTime::from_timestamp(0, 0)]));
        let mut entry = self.clone();
        for row in rows.iter() {
            entry.id = row.get(0);
        }
        Ok(entry)
    }
    fn save(&mut self) -> Result<(), Error> {
        self.updated_at = UTC::now().naive_utc();
        let conn = try!(conn());
        let stmt = try!(conn.prepare("UPDATE entries SET
                                   url         = $2,
                                   title       = $3,
                                   description = $4,
                                   visual_url  = $5,
                                   locale      = $6,
                                   summary     = $7,
                                   content     = $8,
                                   author      = $9,
                                   crawled     = $10,
                                   published   = $11,
                                   updated     = $12,
                                   fingerprint = $13,
                                   origin_id   = $14,
                                   alternate   = $15,
                                   keywords    = $16,
                                   enclosure   = $17,
                                   feed_id     = $18,
                                   created_at  = $19,
                                   updated_at  = $20
                                 WHERE id = $1"));
        let result = stmt.query(&[&self.id,
                                  &self.url,
                                  &self.title,
                                  &self.description,
                                  &self.visual_url,
                                  &self.locale,
                                  &self.summary,
                                  &self.content,
                                  &self.author,
                                  &self.crawled,
                                  &self.published,
                                  &self.updated,
                                  &self.fingerprint,
                                  &self.origin_id,
                                  &self.alternate,
                                  &self.keywords,
                                  &self.enclosure,
                                  &self.feed_id,
                                  &self.created_at,
                                  &self.updated_at]);
        try!(result);
        Ok(())
    }
}

impl Entry {
    pub fn new(url: String) -> Result<Entry, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO entries (url) VALUES ($1) RETURNING id"));
        let rows = try!(stmt.query(&[&url]));
        for row in rows.iter() {
            let entry = Entry {
                id:          row.get(0),
                url:         url,
                title:       None,
                description: None,
                visual_url:  None,
                locale:      None,

                summary:     None,
                content:     None,
                author:      None,
                crawled:     NaiveDateTime::from_timestamp(0, 0),
                published:   NaiveDateTime::from_timestamp(0, 0), // exclude from api response
                updated:     None,
                fingerprint: "".to_string(),
                origin_id:   "".to_string(),
                alternate:   Value::Null,
                keywords:    Value::Null,
                enclosure:   Value::Null,
                feed_id:     None,

                created_at:  UTC::now().naive_utc(),
                updated_at:  UTC::now().naive_utc(),

                tracks:      Vec::new(),
                playlists:   Vec::new(),
                albums:      Vec::new(),
            };
            return Ok(entry);
        }
        Err(Error::Unexpected)
    }

    pub fn find_by_url(url: &str) -> Result<Entry, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare(
            &format!("SELECT {} FROM entries
                        WHERE url = $1", Self::props_str(""))));
        let rows = try!(stmt.query(&[&url]));
        let entries = Entry::rows_to_items(rows);
        if entries.len() > 0 {
            return Ok(entries[0].clone());
        }
        return Err(Error::NotFound)
    }

    pub fn find_or_create_by_url(url: String) -> Result<Entry, Error> {
        match Entry::find_by_url(&url) {
            Ok(entry) => Ok(entry),
            Err(Error::NotFound) => Entry::create_by_url(url),
            Err(e) => Err(e),
        }
    }

    pub fn find_by_feed_id(feed_id: Uuid, newer_than: Option<NaiveDateTime>, page: i64, per_page: i64) -> PaginatedCollection<Entry> {
        let conn = conn().unwrap();
        let stmt = conn.prepare(
            &format!("SELECT {} FROM entries
                        WHERE entries.feed_id = $1 AND entries.published >= $2
                        ORDER BY entries.published DESC
                        LIMIT $4 OFFSET $3",
                     Entry::props_str(""))).unwrap();
        let offset = page * per_page;
        let published = newer_than.unwrap_or(NaiveDateTime::from_timestamp(1000, 0)); // ignore 0 timestamp
        let rows   = stmt.query(&[&feed_id, &published, &offset, &per_page]).unwrap();
        let items  = Self::rows_to_items(rows);
        let mut total: i64 = 0;
        let sql = "SELECT COUNT(*) FROM entries WHERE entries.feed_id = $1";
        for row in conn.query(&sql, &[&feed_id]).unwrap().iter() {
            total = row.get(0);
        }
        PaginatedCollection {
            page:     page,
            per_page: per_page,
            total:    total,
            items:    items,
        }
    }

    pub fn create_by_url(url: String) -> Result<Entry, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO entries (url, published) VALUES ($1, $2) RETURNING id"));
        let rows = try!(stmt.query(&[&url, &NaiveDateTime::from_timestamp(0, 0)]));
        for row in rows.iter() {
            let entry = Entry {
                id:          row.get(0),
                url:         url,
                title:       None,
                description: None,
                visual_url:  None,
                locale:      None,

                summary:     None,
                content:     None,
                author:      None,
                crawled:     UTC::now().naive_utc(),
                published:   UTC::now().naive_utc(),
                updated:     None,
                fingerprint: "".to_string(),
                origin_id:   "".to_string(),
                alternate:   Value::Null,
                keywords:    Value::Null,
                enclosure:   Value::Null,
                feed_id:     None,

                created_at:  UTC::now().naive_utc(),
                updated_at:  UTC::now().naive_utc(),

                tracks:      Vec::new(),
                playlists:   Vec::new(),
                albums:      Vec::new(),
            };
            return Ok(entry);
        }
        Err(Error::Unexpected)
    }

    pub fn is_valid(&self) -> bool {
        self.published.timestamp() >= 1000
    }

    pub fn find_or_create_by_url_if_invalid(url: String) -> Result<Entry, Error> {
        match Entry::find_by_url(&url) {
            Ok(entry) => {
                if !entry.is_valid() {
                    Ok(entry)
                } else {
                    Err(Error::NotFound)
                }
            },
            Err(_) => {
                Entry::create_by_url(url)
            }
        }
    }

    pub fn update_with_feed_entry(&mut self, entry: &feed_rs::Entry) {
        self.title       = entry.title.clone();
        self.summary     = entry.summary.clone();
        self.content     = entry.content.clone();
        self.author      = entry.author.clone();
        self.crawled     = UTC::now().naive_utc();
        self.published   = entry.published;
        self.updated     = entry.updated;
        self.fingerprint = entry.fingerprint.clone();
        self.origin_id   = entry.id.clone();
        self.alternate   = json!(entry.alternate);
        self.keywords    = json!(entry.keywords);
        self.enclosure   = json!(entry.enclosure);

        self.updated_at  = UTC::now().naive_utc();
    }

    pub fn playlistify(&mut self) -> Result<(), Error> {
        let product = try!(scraper::extract(&self.url));
        match product.og_obj {
            Some(og_obj) => {
                if !og_obj.title.is_empty() {
                    self.title = Some(og_obj.title);
                }
                self.description = og_obj.description;
                self.locale      = og_obj.locale;
                self.visual_url  = og_obj.images.first().map(|i| i.url.clone());
            },
            None => (),
        }
        for t in product.tracks {
            let new_track = try!(Track::find_or_create(t.provider, t.identifier.to_string()));
            let mut track = t.clone();
            track.id      = new_track.id;
            try!(track.fetch_detail().save());
            match self.tracks.iter().find(|&t| t.id == track.id) {
                Some(_) => (),
                None    => try!(self.add_track(track.clone())),
            }
        }
        for p in product.playlists {
            let new_playlist = try!(Playlist::find_or_create(p.provider, p.identifier.to_string()));
            let mut playlist = p.clone();
            playlist.id      = new_playlist.id;
            try!(playlist.save());
            match self.playlists.iter().find(|&p| p.id == playlist.id) {
                Some(_) => (),
                None    => try!(self.add_playlist(playlist.clone())),
            }
        }
        for a in product.albums {
            let new_album = try!(Album::find_or_create(a.provider, a.identifier.to_string()));
            let mut album = a.clone();
            album.id      = new_album.id;
            try!(album.save());
            match self.albums.iter().find(|&a| a.id == album.id) {
                Some(_) => (),
                None    => try!(self.add_album(album.clone())),
            }
        }

        Ok(())
    }

    pub fn add_track(&mut self, track: Track) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO track_entries (track_id, entry_id)
                                 VALUES ($1, $2)"));
        try!(stmt.query(&[&track.id, &self.id]));
        self.tracks.push(track);
        Ok(())
    }

    pub fn add_playlist(&mut self, playlist: Playlist) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO playlist_entries (playlist_id, entry_id)
                                 VALUES ($1, $2)"));
        try!(stmt.query(&[&playlist.id, &self.id]));
        self.playlists.push(playlist);
        Ok(())
    }

    pub fn add_album(&mut self, album: Album) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO album_entries (album_id, entry_id)
                                 VALUES ($1, $2)"));
        try!(stmt.query(&[&album.id, &self.id]));
        self.albums.push(album);
        Ok(())
    }
}
<|endoftext|>