rss-tool/src/db.rs

241 lines
6.9 KiB
Rust
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use std::path::PathBuf;
use chrono::FixedOffset;
use rusqlite::{ Connection, Result};
use super::files::*;
use super::net::*;
use chrono::Utc; //Maybe use a different time?
use chrono::DateTime;
/*
Cache is the in-memory database
Any changes/updates are written to file database
*/
struct Item {
title: String,
content: Option<String>,
}
const DB_LOCATION: &str = "rsscar.db";
fn get_db_path() -> PathBuf {
get_data_directory().join(DB_LOCATION)
}
fn get_db() -> Connection{
Connection::open(get_db_path()).unwrap()
}
//url needs to be from the feed URL NOT the url in the channel itself!!
const FEEDS_TABLE_CREATE: &str = "CREATE TABLE IF NOT EXISTS 'feeds' (
'feedID' INTEGER NOT NULL,
'title' TEXT NOT NULL,
'description' TEXT,
'icon' BLOB,
'url' text,
'subscribed' INTEGER NOT NULL default 0,
'last_updated' TEXT ,
PRIMARY KEY('feedID')
);";
const FEEDS_INDEX_CREATE: &str = "CREATE INDEX IF NOT EXISTS 'subscribed_feeds_idx' ON 'feeds' (
'feedID' ASC
) WHERE 'subscribed' = 1;";
/* */
const ITEMS_TABLE_CREATE: &str = "CREATE TABLE IF NOT EXISTS 'items' (
'itemID' INTEGER NOT NULL,
'feedID' INTEGER NOT NULL,
'title' TEXT NOT NULL,
'icon' BLOB,
'url' text not null unique on conflict replace,
'description' TEXT,
'content' TEXT,
'read' INTEGER DEFAULT 0,
PRIMARY KEY('itemID'),
FOREIGN KEY('feedID') REFERENCES 'feeds'('feedID')
);";
const ITEMS_INDEX_CREATE: &str = "CREATE INDEX IF NOT EXISTS 'items_idx' on 'items'('itemID' ASC);";
const ITEMS_FEED_INDEX_CREATE: &str = "CREATE INDEX IF NOT EXISTS 'item_feed_idx' on 'items'('feedID' ASC);";
const DB_RESET: &str = "
drop table items;
drop table feeds;
";
pub fn reset(){
println!("WARNING⚠\nResetting Database");
let conn = get_db();
match conn.execute_batch(DB_RESET) {
Ok(_) => {println!("Database successfully wiped.")}
Err(e) => {panic!("Error erasing database.\nError: {0}",e)}
}
conn.close().unwrap();
initialize();
}
pub fn initialize() {
let path = get_db_path();
println!("Database at {} initialized", path.to_string_lossy());
let conn = get_db();
conn.execute(FEEDS_TABLE_CREATE, []).unwrap();
conn.execute(FEEDS_INDEX_CREATE, []).unwrap();
conn.execute(ITEMS_TABLE_CREATE, []).unwrap();
conn.execute(ITEMS_INDEX_CREATE, []).unwrap();
conn.execute(ITEMS_FEED_INDEX_CREATE,[]).unwrap();
conn.close().unwrap();
println!("Database Initialized.")
}
pub fn add_feed(url: &str) {
let conn = Connection::open(get_db_path()).unwrap();
let feed = load_rss(url).unwrap();
let new_feed = feed.clone();
let time = Utc::now().to_rfc2822();
conn.execute(
"insert into feeds(title,url,description,last_updated) values(?1,?2,?3,?4)",
[feed.title, url.to_string(), feed.description, time],
)
.unwrap();
let mut stmt = conn.prepare("select feedID from feeds where url=?1").unwrap();
let id: usize = stmt.query_row([url],|row| {
row.get(0)
}).unwrap();
//need to get the feed_id from the DB and then make sure items are mapped to feed
store_items(new_feed,id);
}
pub fn store_items(feed: rss::Channel,feed_id: usize) {
let conn = Connection::open(get_db_path()).unwrap();
feed.items.into_iter().for_each(|i: rss::Item| {
conn.execute(
"insert into items(url,title,description,content,feedID)
values(?1,?2,?3,?4,?5)",
[
i.link,
i.title,
i.description,
i.content,
Some(feed_id.to_string())
],
)
.ok();
});
conn.close().unwrap();
}
pub fn return_item() -> String {
let conn = Connection::open(get_db_path()).unwrap();
let item = conn
.query_row(
"select title,content from items where rowid=?1",
[],
|row| {
Ok(Item {
title: row.get(0).unwrap(),
content: row.get(1).unwrap(),
})
},
)
.unwrap();
match item.content {
Some(content) => content,
None => panic!(),
}
}
pub struct FeedItem {
pub item_id: usize,
pub title: String,
pub url: String,
pub icon: Option<String>,
pub description: Option<String>,
pub content: Option<String>
//date missing! needed for ordering!!!
}
pub fn get_feed_items(feed_id: usize) -> Vec<FeedItem>{
let conn = get_db();
let mut stmt = conn.prepare("select itemID,title,url,icon,description,content from items where feedID = ?1").unwrap();
let items:Result<Vec<FeedItem>> = stmt.query_map([feed_id], |row| {
Ok(FeedItem{
item_id: row.get(0).unwrap(),
title: row.get(1).unwrap(),
url: row.get(2).unwrap(),
icon: row.get(3).unwrap(),
description: row.get(4).unwrap(),
content: row.get(5).unwrap()
})
}).unwrap().collect();
match items {
Ok(i) => {i},
Err(_) => {panic!("No items for this feed\nFeedID:{}",feed_id)}
}
}
pub fn get_item(item_id: usize) -> FeedItem {
let conn = get_db();
let mut stmt = conn.prepare("select itemID,title,url,icon,description,content from items where itemID = ?1").unwrap();
let item:FeedItem = stmt.query_one([item_id],|row| {
Ok(
FeedItem{
item_id: row.get(0).unwrap(),
title: row.get(1).unwrap(),
url: row.get(2).unwrap(),
icon: row.get(3).unwrap(),
description: row.get(4).unwrap(),
content: row.get(5).unwrap(),
})
}).unwrap();
item
}
pub struct Feed {
pub feed_id: usize,
pub title: String,
pub description: Option<String>,
pub icon: Option<String>,
pub url: String,
pub subscribed: bool,
pub last_updated: Option<DateTime<Utc>>,
}
fn time_string_conversion(str: String) -> Option<DateTime<Utc>>{
match DateTime::parse_from_rfc2822(&str) {
Ok(dt) => {Some(dt.to_utc())},
Err(_) => {None}
}
}
pub fn get_feeds() -> Vec<Feed> {
let conn = get_db();
let mut stmt = conn.prepare("select feedID,title,description,icon,url,subscribed,last_updated from feeds order by last_updated desc").unwrap();
let rows: Result<Vec<Feed>> = stmt
.query_map([], |row| {
Ok(Feed {
feed_id: row.get(0).unwrap(),
title: row.get(1).unwrap(),
description: row.get(2).unwrap(),
icon: row.get(3).unwrap(),
url: row.get(4).unwrap(),
subscribed: row.get::<_,bool>(5).unwrap(),
last_updated:time_string_conversion(row.get(6).unwrap()),
})
}).unwrap().collect();
match rows {
Ok(r) => {
r
}
Err(e) => {panic!("No idea what causes this")}
}
}
struct ReturnedFeedURLs {
url: String,
}
pub fn update_feeds() {
todo!()
}