1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use chrono::{DateTime, TimeZone, Utc};
use serde::{Deserialize, Serialize};
use std::ops::Deref;

/// An ID as defined by the GraphQL specification
///
/// Represented as a string, but can be converted _to_ from an integer as well.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct ID(String);

impl From<String> for ID {
    fn from(s: String) -> ID {
        ID(s)
    }
}

impl ID {
    /// Construct a new ID from anything implementing `Into<String>`
    pub fn new<S: Into<String>>(value: S) -> Self {
        ID(value.into())
    }
}

impl Deref for ID {
    type Target = str;

    fn deref(&self) -> &str {
        &self.0
    }
}

#[cfg(feature = "graphql")]
impl From<juniper::ID> for ID {
    fn from(id: juniper::ID) -> ID {
        ID(id.to_string())
    }
}

#[cfg(feature = "graphql")]
impl From<ID> for juniper::ID {
    fn from(id: ID) -> juniper::ID {
        juniper::ID::new(id.to_string())
    }
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct NodeDetails {
    pub id: ID,
    date_created: i64,
    date_modified: i64,
    created_by_id: ID,
    updated_by_id: ID,
}

impl NodeDetails {
    pub fn id(&self) -> ID {
        self.id.to_owned()
    }

    pub fn date_created(&self) -> DateTime<Utc> {
        Utc.timestamp(self.date_created, 0)
    }

    pub fn date_modified(&self) -> DateTime<Utc> {
        Utc.timestamp(self.date_modified, 0)
    }

    pub fn created_by_id(&self) -> ID {
        self.created_by_id.to_owned()
    }

    pub fn updated_by_id(&self) -> ID {
        self.updated_by_id.to_owned()
    }
}

pub trait Node {
    fn node(&self) -> &NodeDetails;
}

/// Returns the first item in an array of Nodes
/// Useful for when you have two fields one that is embedded in a list and one that is not
/// ie. my_pets and favorite_pet - you probably don't want to embed it twice
pub fn get_selected_or_first<T>(selected: &Option<ID>, list: &Option<Vec<T>>) -> Option<T>
where
    T: Clone + Node,
{
    match selected {
        Some(id) => {
            // find it in the array
            match list {
                Some(list_items) => {
                    let item: Option<&T> = list_items.iter().find(|x| x.node().id == *id);
                    match item {
                        Some(i) => Some(i.clone()),
                        None => None,
                    }
                }
                None => None,
            }
        }
        None => {
            // take the first one if nothing was picked
            if let Some(list_items) = &list {
                if !list_items.is_empty() {
                    Some(list_items[0].clone())
                } else {
                    None
                }
            } else {
                None
            }
        }
    }
}