use crate::{
data::{Data, Page, Table},
model::{
article::{self, Article},
board::{self, Board},
session::ChatSession,
},
};
use std::{
path::{Path, PathBuf},
time::{SystemTime, UNIX_EPOCH},
};
const DIR: &str = ".cydonia";
const PAGE: i64 = 200;
pub struct Project {
pub path: PathBuf,
pub sessions: Vec<ChatSession>,
pub active: Option<u64>,
pub boards: Vec<Board>,
pub board: Option<usize>,
pub articles: Vec<Article>,
pub article: Option<usize>,
pub data: Option<Data>,
pub tables: Vec<Table>,
pub table: Option<usize>,
pub page: Option<Page>,
pub expanded: bool,
pub archive_open: bool,
}
impl Project {
pub fn new(path: PathBuf) -> Self {
let mut this = Self {
boards: board::list(&path),
articles: article::list(&path),
data: Data::attach(&path),
path,
sessions: Vec::new(),
active: None,
board: None,
article: None,
tables: Vec::new(),
table: None,
page: None,
expanded: true,
archive_open: false,
};
this.reload_tables();
this
}
pub fn reload_tables(&mut self) {
let open = self
.table
.and_then(|ix| self.tables.get(ix))
.map(|table| table.key.clone());
self.tables = self
.data
.as_ref()
.and_then(|data| data.list().ok())
.unwrap_or_default();
self.table = match open.and_then(|key| self.position(&key)) {
found @ Some(_) => found,
None => self.table.filter(|ix| *ix < self.tables.len()),
};
self.reload_page();
}
fn position(&self, key: &str) -> Option<usize> {
self.tables.iter().position(|table| table.key == key)
}
pub fn reload_page(&mut self) {
let key = self
.table
.and_then(|ix| self.tables.get(ix))
.map(|table| table.key.clone());
self.page = match (key, self.data.as_ref()) {
(Some(key), Some(data)) => data.read(&key, None, false, PAGE, 0).ok(),
_ => None,
};
}
pub fn name(&self) -> String {
self.path
.file_name()
.map(|name| name.to_string_lossy().into_owned())
.unwrap_or_else(|| self.path.display().to_string())
}
pub fn session(&self, id: u64) -> Option<&ChatSession> {
self.sessions.iter().find(|chat| chat.id == id)
}
pub fn session_mut(&mut self, id: u64) -> Option<&mut ChatSession> {
self.sessions.iter_mut().find(|chat| chat.id == id)
}
pub fn active_session(&self) -> Option<&ChatSession> {
self.active.and_then(|id| self.session(id))
}
}
pub fn stamp() -> u128 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|since| since.as_millis())
.unwrap_or_default()
}
pub fn written(path: &Path) -> u128 {
std::fs::metadata(path)
.and_then(|meta| meta.modified())
.ok()
.and_then(|time| time.duration_since(UNIX_EPOCH).ok())
.map_or_else(stamp, |since| since.as_millis())
}
pub fn dir(project: &Path) -> PathBuf {
project.join(DIR)
}
pub fn init(project: &Path) -> std::io::Result<PathBuf> {
let dir = dir(project);
std::fs::create_dir_all(&dir)?;
let ignore = dir.join(".gitignore");
if !ignore.exists() {
std::fs::write(&ignore, "*\n")?;
}
Ok(dir)
}