use serde::{Deserialize, Serialize};
use serde_json::{self, json, Value};
use std::fs::{self, OpenOptions};
use std::io::{self, Read, Write};
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(untagged)]
pub enum Context {
Plain(String),
Complex(Vec<Value>),
}
impl Default for Context {
fn default() -> Self {
Context::Plain("https://www.w3.org/ns/activitystreams".to_string())
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub enum CollectionType {
#[default]
OrderedCollection,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Collection {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "@context")]
pub context: Option<Context>,
#[serde(rename = "type")]
pub kind: CollectionType,
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub first: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prev: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub part_of: Option<String>,
pub total_items: i32,
pub ordered_items: Vec<Value>,
}
impl Default for Collection {
fn default() -> Self {
Collection {
context: Some(Context::default()),
kind: CollectionType::default(),
id: None,
first: None,
last: None,
prev: None,
next: None,
part_of: None,
total_items: 0,
ordered_items: vec![],
}
}
}
impl From<Vec<Value>> for Collection {
fn from(values: Vec<Value>) -> Self {
Collection {
ordered_items: values.clone(),
total_items: values.len() as i32,
..Default::default()
}
}
}
impl Collection {
pub fn id(&mut self, id: String) -> &Self {
self.id = Some(id);
self
}
}
fn read_collection_from_file(filename: &str) -> io::Result<Collection> {
let path = format!(
"{}/.sofapub/data/{}",
std::env::var("HOME").unwrap(),
filename
);
let mut file_content = String::new();
fs::File::open(path)?.read_to_string(&mut file_content)?;
let ordered_items: Vec<Value> = serde_json::from_str(&file_content)?;
let collection = Collection::from(ordered_items);
Ok(collection)
}
fn write_collection_to_file(collection: &Collection, filename: &str) -> io::Result<()> {
let path = format!(
"{}/.sofapub/data/{}",
std::env::var("HOME").unwrap(),
filename
);
let content = serde_json::to_string_pretty(&collection.ordered_items)?;
let mut file = OpenOptions::new().write(true).truncate(true).open(path)?;
file.write_all(content.as_bytes())?;
Ok(())
}
pub fn add_id_to_collection(id: String, filename: &str) -> io::Result<()> {
let mut collection = read_collection_from_file(filename)?;
if !collection.ordered_items.contains(&json!(id)) {
collection.ordered_items.push(json!(id));
collection.total_items = collection.ordered_items.len() as i32;
}
write_collection_to_file(&collection, filename)
}
pub fn remove_id_from_collection(id: &str, filename: &str) -> io::Result<()> {
let mut collection = read_collection_from_file(filename)?;
collection.ordered_items.retain(|item| item != &json!(id));
collection.total_items = collection.ordered_items.len() as i32;
write_collection_to_file(&collection, filename)
}