about summary refs log tree commit diff
path: root/src/database/memory.rs
blob: a4cf5a9753dc0472a2b7638f9ed6f8ad1eca7eb2 (plain) (blame)
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
use async_trait::async_trait;
use std::collections::HashMap;
use std::sync::Arc;
use async_std::sync::RwLock;
use futures_util::FutureExt;
use serde_json::json;

use crate::database::{Storage, Result, StorageError, ErrorKind, MicropubChannel};
use crate::indieauth::User;

#[derive(Clone)]
pub struct MemoryStorage {
    pub mapping: Arc<RwLock<HashMap<String, serde_json::Value>>>,
    pub channels: Arc<RwLock<HashMap<String, Vec<String>>>>
}

#[async_trait]
impl Storage for MemoryStorage {
    async fn read_feed_with_limit<'a>(&self, url: &'a str, after: &'a Option<String>, limit: usize, user: &'a Option<String>) -> Result<Option<serde_json::Value>> {
        todo!()
    }

    async fn update_post<'a>(&self, url: &'a str, update: serde_json::Value) -> Result<()> {
        todo!()
    }

    async fn delete_post<'a>(&self, url: &'a str) -> Result<()> {
        self.mapping.write().await.remove(url);
        Ok(())
    }

    async fn post_exists(&self, url: &str) -> Result<bool> {
        return Ok(self.mapping.read().await.contains_key(url))
    }

    async fn get_post(&self, url: &str) ->Result<Option<serde_json::Value>> {
        let mapping = self.mapping.read().await;
        match mapping.get(url) {
            Some(val) => {
                if let Some(new_url) = val["see_other"].as_str() {
                    match mapping.get(new_url) {
                        Some(val) => Ok(Some(val.clone())),
                        None => {
                            drop(mapping);
                            self.mapping.write().await.remove(url);
                            Ok(None)
                        }
                    }
                } else {
                    Ok(Some(val.clone()))
                }
            },
            _ => Ok(None)
        }
    }

    async fn get_channels(&self, user: &User) -> Result<Vec<MicropubChannel>> {
        match self.channels.read().await.get(&user.me.to_string()) {
            Some(channels) => Ok(futures_util::future::join_all(channels.iter()
                .map(|channel| self.get_post(channel)
                    .map(|result| result.unwrap())
                    .map(|post: Option<serde_json::Value>| {
                        if let Some(post) = post {
                            Some(MicropubChannel {
                                uid: post["properties"]["uid"][0].as_str().unwrap().to_string(),
                                name: post["properties"]["name"][0].as_str().unwrap().to_string()
                            })
                        } else { None }
                    })
                ).collect::<Vec<_>>()).await.into_iter().filter_map(|chan| chan).collect::<Vec<_>>()),
            None => Ok(vec![])
        }
        
    }

    async fn put_post<'a>(&self, post: &'a serde_json::Value) -> Result<()> {
        let mapping = &mut self.mapping.write().await;
        let key: &str;
        match post["properties"]["uid"][0].as_str() {
            Some(uid) => key = uid,
            None => return Err(StorageError::new(ErrorKind::Other, "post doesn't have a UID"))
        }
        mapping.insert(key.to_string(), post.clone());
        if post["properties"]["url"].is_array() {
            for url in post["properties"]["url"].as_array().unwrap().iter().map(|i| i.as_str().unwrap().to_string()) {
                if &url != key {
                    mapping.insert(url, json!({"see_other": key}));
                }
            }
        }
        if post["type"].as_array().unwrap().iter().any(|i| i == "h-feed") {
            // This is a feed. Add it to the channels array if it's not already there.
            println!("{:#}", post);
            self.channels.write().await.entry(post["properties"]["author"][0].as_str().unwrap().to_string()).or_insert(vec![]).push(key.to_string())
        }
        Ok(())
    }
}

impl MemoryStorage {
    pub fn new() -> Self {
        Self {
            mapping: Arc::new(RwLock::new(HashMap::new())),
            channels: Arc::new(RwLock::new(HashMap::new()))
        }
    }
}