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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
extern crate reqwest;
#[macro_use]
extern crate maplit;

use serde::*;
use std::collections::HashSet;

const MAX_HASHTAGS: usize = 30;

#[derive(Deserialize)]
struct JsonResult {
    word: String,
    score: usize,
}

pub struct Instagen {
    output_tags: HashSet<String>,
}

impl Instagen {
    pub fn generate(input_hashtags: Vec<&str>) -> Self {
        let mut instagen = Self {
            output_tags: HashSet::new(),
        };
        let mut similar_words: Vec<Vec<String>> = Vec::new();

        for hashtag in &input_hashtags {
            // Ask a json with similar words to the datamuse APIs
            let url = format!("https://api.datamuse.com/words?rel_syn={}", hashtag);
            let json_result: Vec<JsonResult> = reqwest::get(&url)
                .expect("Could not fetch JSON from datamuse")
                .json()
                .expect("Could not parse JSON");
            let words: Vec<String> = json_result
                .iter()
                .map(|result| result.word.clone())
                .collect();

            similar_words.push(words);
        }

        instagen.process_hashtags(&input_hashtags, &mut similar_words);

        instagen
    }

    fn process_hashtags(
        &mut self,
        input_hashtags: &Vec<&str>,
        similar_words: &mut Vec<Vec<String>>,
    ) {
        // Add user hashtags
        for input_hashtag in input_hashtags {
            self.output_tags.insert(input_hashtag.to_string());
        }

        let mut counter = 0;
        // Add hashtags from suggestions until max size
        while counter < MAX_HASHTAGS {
            for words in &mut *similar_words {
                if let Some(word) = words.pop() {
                    self.output_tags.insert(word.to_string());
                    counter += 1;
                }
            }
            counter += 1;
        }
    }

    pub fn to_hashtags(self) -> String {
        // Print all hashtags with a # prefix in a string
        // Also, removes white spaces from tags
        self.output_tags
            .clone()
            .into_iter()
            .map(|hashtag| format!("#{} ", hashtag.replace(" ", "")))
            .collect::<String>()
    }

    pub fn to_hashset(self) -> HashSet<String> {
        self.output_tags
    }
}

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

    #[test]
    fn test_load_sample_tags() {
        let result = Instagen::generate(vec!["sweden", "summer", "sea"]);

        assert_eq!(
            result.output_tags,
            hashset! {String::from("sweden"), String::from("sverige"), String::from("ocean"), String::from("summertime"), String::from("sea"), String::from("summer")}
        );
    }

    #[test]
    fn test_load_sample_tags_red() {
        let result = Instagen::generate(vec!["sweden", "summer", "sea"]);

        assert_ne!(result.output_tags, hashset! {String::from("italy")});
    }

    #[test]
    fn test_load_empty() {
        let result = Instagen::generate(vec![""]);

        assert_eq!(result.output_tags, hashset! {String::from("")});
    }

    #[test]
    fn test_load_empty_red() {
        let result = Instagen::generate(vec![""]);

        assert_ne!(result.output_tags, hashset! {String::from("italy")});
    }

    #[test]
    fn test_load_sample_tags_return_hashset() {
        let result = Instagen::generate(vec!["cow"]).to_hashset();

        assert_eq!(
            result,
            hashset! {String::from("cow"), String::from("overawe"), String::from("moo-cow")}
        );
    }

    #[test]
    fn test_load_sample_tags_return_hashset_red() {
        let result = Instagen::generate(vec!["cow"]).to_hashset();

        assert_ne!(result, hashset! {String::from("dog")});
    }

    #[test]
    fn test_load_sample_tags_return_hashtags() {
        let result = Instagen::generate(vec!["kitten"]).to_hashtags();

        assert!(result.contains("#kitty"));
    }

    #[test]
    fn test_load_sample_tags_return_hashtags_red() {
        let result = Instagen::generate(vec!["kitten"]).to_hashtags();

        assert!(!result.contains("#italy"));
    }
}