From 1933a99b4335a98d52d5edd126acb4155d9026fd Mon Sep 17 00:00:00 2001 From: Kostas Kolivas Date: Wed, 20 Sep 2023 16:31:36 +0300 Subject: [PATCH] Introduce Event struct This struct contains methods for deserializing JSON strings into Events, as well as inserting/retrieving Events from the DB Ref #6 --- src/entities/event.rs | 101 ++++++++++++++++++++++++++++++++---------- src/errors.rs | 8 +++- 2 files changed, 84 insertions(+), 25 deletions(-) diff --git a/src/entities/event.rs b/src/entities/event.rs index 751967b..68c45e4 100644 --- a/src/entities/event.rs +++ b/src/entities/event.rs @@ -3,31 +3,49 @@ use anyhow::Result; use chrono::{DateTime, Local}; use r2d2_postgres::postgres::{Row, Transaction}; -use crate::database::Insert; +use serde_json::Value; +use crate::database::Insert; +use crate::errors::DeserializationError; + +const DATETIME_FMT: &str = "%Y/%m/%d-%H:%M:%S"; + +/// Responsible for the deserialization as well as DB insertion of +/// events. Contains the following fields: +/// +/// +/// id - A unique identifier for this event. Could be of different format based on the source +/// url - The url from which this file was retrieved +/// size - The size of the file (in bytes) +/// source - The source in which the paste was found +/// raw_content - The entire content of the file as retrieved from the source +/// filename - The name of the file as retrieved from the source +/// creator - The username of the creator +/// created_at - Time at which the paste was created +/// discovered_at - Time at which the paste was scraped #[derive(Debug)] pub struct Event { - /// A unique identifier for this event. Could be of different format based on the source id: Option, - /// The url from which this file was retrieved url: String, - /// The size of the file (in bytes) - size: i64, - /// The source in which the paste was found + size: usize, source: String, - /// The entire content of the file as retrieved from the source raw_content: String, - /// The name of the file as retrieved from the source filename: String, - /// The username of the creator creator: String, - /// Time at which the paste was created created_at: DateTime, - /// Time at which the paste was discovered discovered_at: DateTime } impl Insert for Event { + /// Insert the event into the DB + /// + /// # Arguments + /// + /// * conn - A currently open (uncommitted) DB transaction + /// + /// # Returns + /// + /// An empty Result fn insert(&mut self, conn: &mut Transaction) -> Result<()> { let stmt = " INSERT INTO events @@ -53,7 +71,7 @@ impl Insert for Event { &[ &self.source, &self.url, - &self.size, + &(self.size as i64), &self.raw_content, &self.filename, &self.creator, @@ -68,28 +86,53 @@ impl Insert for Event { } impl Event { + pub fn from_json_str(json_str: &str) -> Result { + let json: Value = serde_json::from_str(json_str)?; + + let url = Self::get_str(&json, "url")?; + let size = Self::get_i64(&json, "size")? as usize; + let source = Self::get_str(&json, "source")?; + let raw_content = Self::get_str(&json, "raw_content")?; + let filename = Self::get_str(&json, "filename")?; + let creator = Self::get_str(&json, "creator")?; + let created_at: DateTime = + match Self::get_str(&json, "created_at") { + Ok(c) => DateTime::parse_from_str(&c, DATETIME_FMT)?.into(), + Err(e) => return Err(e) + + }; + let discovered_at: DateTime = + match Self::get_str(&json, "discovered_at") { + Ok(c) => DateTime::parse_from_str(&c, DATETIME_FMT)?.into(), + Err(e) => return Err(e) + }; + + Ok(Self::new(&url, size, &source, &raw_content, &filename, &creator, created_at, discovered_at)) + } + pub fn new( url: &str, - size: i64, + size: usize, source: &str, raw_content: &str, filename: &str, creator: &str, + created_at: DateTime, discovered_at: DateTime ) -> Self { - Self::create(None, url, size, source, raw_content, filename, creator, None, discovered_at) + Self::create( None, url, size, source, raw_content, filename, creator, created_at, discovered_at) } pub fn from_row(row: Row) -> Self { Self::create( Some(row.get("id")), row.get("url"), - row.get("size"), + row.get::<&str, i64>("size") as usize, row.get("source"), row.get("raw_content"), row.get("filename"), row.get("creator"), - Some(row.get("created_at")), + row.get("created_at"), row.get("discovered_at") ) } @@ -102,7 +145,7 @@ impl Event { &self.url } - pub fn size(&self) -> i64 { + pub fn size(&self) -> usize { self.size } @@ -134,19 +177,14 @@ impl Event { fn create( id: Option, url: &str, - size: i64, + size: usize, source: &str, raw_content: &str, filename: &str, creator: &str, - created_at: Option>, + created_at: DateTime, discovered_at: DateTime ) -> Self { - let created_at = match created_at { - Some(ca) => ca, - None => Local::now() - }; - Self { id, url: url.to_owned(), @@ -159,4 +197,19 @@ impl Event { discovered_at } } + + fn get_str(json: &Value, field_name: &str) -> Result { + match json[field_name].as_str() { + Some(u) => Ok(u.to_owned()), + None => return Err(DeserializationError::NoValueError(field_name.to_string()).into()) + } + } + + fn get_i64(json: &Value, field_name: &str) -> Result { + match json[field_name].as_i64() { + Some(u) => Ok(u.to_owned()), + None => return Err(DeserializationError::NoValueError(field_name.to_string()).into()) + } + + } } diff --git a/src/errors.rs b/src/errors.rs index e199525..1069708 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -8,4 +8,10 @@ pub enum ConfigurationError { NoYaraRulesError, #[error("Number of workers cannot be negative")] NegativeWorkersError -} \ No newline at end of file +} + +#[derive(Error, Debug)] +pub enum DeserializationError { + #[error("Empty '{0}' value when deserializing event")] + NoValueError(String) +}