website/src/bin/generate_rss.rs

112 lines
3.9 KiB
Rust

//! # RSS Generator
//!
//! A custom RSS generator. It crawls the template sub-directories containing
//! articles for syndication, extracts relevant information and writes the items
//! to `./static/feed.rss`.
//!
extern crate regex;
extern crate rss;
use regex::Regex;
use rss::{ChannelBuilder, Item};
use std::{error, fs, fs::File, io::prelude::*};
fn main() -> Result<(), Box<dyn error::Error>> {
// create rss channel for mycelial.technology
let mut channel = ChannelBuilder::default()
.title("mycelial technology")
.link("https://mycelial.technology")
.description(
"glyph's RSS feed. Biophilic musings on carbon-based and silicon-based technologies.",
)
.build()?;
// list template directories containing articles for syndication
let bacteria = "./site/bacteria";
let computers = "./site/computers";
let fungi = "./site/fungi";
let plants = "./site/plants";
// add directories to a vector
let dirs = vec![bacteria, computers, fungi, plants];
// create vectors for item fields
let mut titles = Vec::new();
let mut pub_dates = Vec::new();
let mut urls = Vec::new();
let mut articles = Vec::new();
// loop through template directories and extract item field values
for dir in dirs {
for entry in fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if !path.ends_with("index.html") {
// populate item url vector from article filenames
let re_url = Regex::new("./site/(.*).html")?;
let caps_url = re_url.captures(
path.to_str()
.expect("Failed to convert file path to string slice for regex capture"),
);
if let Some(url) = caps_url {
let article_url = url[1].replace("_", "-");
let full_url = format!("https://mycelial.technology/{}", article_url);
urls.push(full_url);
};
// open the file (article) and read it to a string
let mut file = File::open(path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
// populate item title vector from article heading
let re_h2 = Regex::new("<h2>(.*)</h2>")?;
let caps_h2 = re_h2.captures(&contents);
if let Some(title) = caps_h2 {
titles.push(title[1].to_string());
};
// populate pub_date vector from first italic element in article
let re_i = Regex::new("<i>(.*)</i>")?;
let caps_i = re_i.captures(&contents);
if let Some(date) = caps_i {
pub_dates.push(date[1].to_string());
};
// populate article content vector from article element
let re_article = Regex::new(r"<article>[\s\S]*</article>")?;
let caps_article = re_article.captures(&contents);
if let Some(content) = caps_article {
articles.push(content[0].to_string());
};
}
}
}
// get length of titles vector (serves as proxy for total number of articles)
let library = titles.len();
// create vector for channel items
let mut items = Vec::new();
// loop through vectors of item fields and create items accordingly
for x in 0..library {
let mut item = Item::default();
item.set_title(titles[x].to_string());
item.set_link(urls[x].to_string());
item.set_pub_date(pub_dates[x].to_string());
item.set_description(articles[x].to_string());
items.push(item)
}
// add the items to the channel
channel.set_items(items);
// write the channel to file
let rss_file = File::create("./static/feed.rss")?;
channel.write_to(rss_file)?;
Ok(())
}