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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
use std::collections::HashMap;
use std::net::IpAddr;

use anyhow::Result;

use async_trait::async_trait;

pub use rapidquery::Expression;

use serde::{Deserialize, Serialize};

use crate::{BlobInfo, BlobMeta};

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct Hit {
    pub id: String,
    pub meta: BlobMeta,
    pub url: String,
}

impl Hit {
    pub fn new(id: String, meta: BlobMeta, url: String) -> Self {
        Self { id, meta, url }
    }
}

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct FacetResponse {
    pub tags: HashMap<String, u64>,
    pub meta: HashMap<String, HashMap<String, u64>>,
}

/// The results of a query.
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct QueryResponse {
    /// The number of hits returned with this response.
    pub count: usize,

    /// The total number of hits.
    pub total: usize,

    /// The query hits.
    pub hits: Vec<Hit>,

    /// The facets computed for this query.
    ///
    /// Returned only if requested with the query.
    pub facets: Option<FacetResponse>,
}

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
pub enum RedirectInfo {
    Automatic {
        public_address: IpAddr,
        local_address: IpAddr,
        subnet_mask: IpAddr,
    },
    Static {
        static_address: IpAddr,
    },
}

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct StorageNodeInfo {
    pub id: String,
    pub redirect_info: RedirectInfo,
    pub port: u16,
}

/// Data sent back to the storage node from the directory.
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct StorageNodeResponseData {
    pub rebuild_requested: bool,
}

#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct MetadataList {
    pub tags: HashMap<String, usize>,
    pub meta: HashMap<String, HashMap<String, usize>>,
}

/// A query that can be sent to a Menmos cluster.
#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct Query {
    /// The query expression.
    pub expression: Expression,
    pub from: usize,
    pub size: usize,
    pub sign_urls: bool,
    pub facets: bool, // TODO: Permit requesting facets for specific tags instead of doing it for all.
}

impl Query {
    pub fn with_expression<S: Into<String>>(mut self, expression: S) -> Result<Self> {
        self.expression = Expression::parse(expression.into())?;
        Ok(self)
    }

    pub fn and_tag<S: Into<String>>(mut self, tag: S) -> Self {
        let new_expr = Expression::Tag { tag: tag.into() };
        self.expression = Expression::And {
            and: (Box::from(self.expression), Box::from(new_expr)),
        };
        self
    }

    pub fn and_meta<K: Into<String>, V: Into<String>>(mut self, k: K, v: V) -> Self {
        let new_expr = Expression::KeyValue {
            key: k.into(),
            value: v.into(),
        };
        self.expression = Expression::And {
            and: (Box::from(self.expression), Box::from(new_expr)),
        };
        self
    }

    pub fn and_parent<P: Into<String>>(mut self, p: P) -> Self {
        let new_expr = Expression::Parent { parent: p.into() };
        self.expression = Expression::And {
            and: (Box::from(self.expression), Box::from(new_expr)),
        };
        self
    }

    pub fn with_from(mut self, f: usize) -> Self {
        self.from = f;
        self
    }

    pub fn with_size(mut self, s: usize) -> Self {
        self.size = s;
        self
    }

    pub fn with_facets(mut self, f: bool) -> Self {
        self.facets = f;
        self
    }
}

impl Default for Query {
    fn default() -> Self {
        Query {
            expression: Default::default(),
            from: 0,
            size: 30,
            sign_urls: true,
            facets: false,
        }
    }
}

#[async_trait]
pub trait DirectoryNode {
    async fn add_blob(&self, blob_id: &str, info: BlobInfo) -> Result<StorageNodeInfo>;
    async fn get_blob_meta(&self, blob_id: &str, user: &str) -> Result<Option<BlobInfo>>;
    async fn index_blob(&self, blob_id: &str, meta: BlobInfo, storage_node_id: &str) -> Result<()>;
    async fn delete_blob(&self, blob_id: &str, username: &str) -> Result<Option<StorageNodeInfo>>;

    async fn register_storage_node(&self, def: StorageNodeInfo) -> Result<StorageNodeResponseData>;
    async fn get_blob_storage_node(&self, blob_id: &str) -> Result<Option<StorageNodeInfo>>;

    async fn commit(&self) -> Result<()>;
    async fn start_rebuild(&self) -> Result<()>;
    async fn rebuild_complete(&self, storage_node_id: &str) -> Result<()>;

    async fn query(&self, q: &Query, username: &str) -> Result<QueryResponse>;
    async fn list_metadata(
        &self,
        tags: Option<Vec<String>>,
        meta_keys: Option<Vec<String>>,
        username: &str,
    ) -> Result<MetadataList>;
    async fn list_storage_nodes(&self) -> Result<Vec<StorageNodeInfo>>;

    async fn login(&self, user: &str, password: &str) -> Result<bool>;
    async fn register(&self, user: &str, password: &str) -> Result<()>;
    async fn has_user(&self, user: &str) -> Result<bool>;
}