Initial commit

This commit is contained in:
RaphyJake 2021-11-30 07:09:31 +01:00
parent a75681ff11
commit 21f3205989
5 changed files with 356 additions and 2 deletions

2
.gitignore vendored
View File

@ -1 +1,3 @@
/target
build
Cargo.lock

View File

@ -6,3 +6,5 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
chrono = "0.4"
atom_syndication = "0.11"

0
README.md Normal file
View File

View File

@ -1,3 +1,350 @@
fn main() {
println!("Hello, world!");
use std::{
env,
fs::{self, copy, create_dir, read_to_string, write},
io,
io::{Error, ErrorKind},
path::{Path, PathBuf},
};
use chrono::{DateTime, FixedOffset, Local, NaiveDate, TimeZone};
use atom_syndication::{Entry, Feed, Link, Person};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
struct GemEntry {
date: NaiveDate,
title: String,
dir: PathBuf,
olddir: PathBuf,
}
impl GemEntry {
fn linkline(&self) -> String {
return format!("=> {} {}", self.getlink(), self.title.replace("_", " "));
}
fn getlink(&self) -> String {
let p = &self.dir;
let q: PathBuf = p.iter().skip(1).collect();
let tmp = q.into_os_string().into_string().unwrap();
tmp
}
}
#[derive(Debug, Clone)]
struct Nonentry {
dir: PathBuf,
olddir: PathBuf,
}
#[derive(Debug, Clone)]
struct Index {
dir: PathBuf,
content: String,
}
#[derive(Debug)]
struct Gemdir {
root: PathBuf, // current dir
index: Option<Index>, // has a index.gmi file
curentries: Vec<GemEntry>, // .gmi files in current folder that needs to be indexed
curfolder: Vec<Nonentry>, // .gmi files that must be copied without indexing
subfolders: Vec<Gemdir>, // .subfolders
}
impl Gemdir {
/// Changes the root dir of a Gemdir.
pub fn changeroot(&mut self, dir: &Path) {
let old = &self.root.clone();
self.chanrgerootrecurse(dir, old);
}
fn chanrgerootrecurse(&mut self, dir: &Path, old: &PathBuf) {
let p = self.root.strip_prefix(old).unwrap();
self.root = dir.join(p);
if let Some(p) = &self.index {
let t = p.dir.strip_prefix(old).unwrap();
let mut p2 = p.clone();
p2.dir = dir.join(t);
self.index = Some(p2);
}
for i in 0..self.curentries.len() {
let p = self.curentries[i].dir.strip_prefix(old).unwrap().clone();
self.curentries[i].dir = dir.join(p);
}
for i in 0..self.curfolder.len() {
let p = self.curfolder[i].dir.strip_prefix(old).unwrap().clone();
self.curfolder[i].dir = dir.join(p);
}
for i in 0..self.subfolders.len() {
self.subfolders[i].chanrgerootrecurse(dir, old);
}
}
fn getsubfiles(&self) -> Vec<GemEntry> {
let mut res = self.curentries.clone();
for sdir in &self.subfolders {
let mut v2 = sdir.getsubfiles();
res.append(&mut v2);
}
res.sort();
res.reverse();
return res;
}
fn indexallsubs(&self) -> String {
let list: Vec<GemEntry> = self.getsubfiles();
let lines: Vec<String> = list.into_iter().map(|x| x.linkline()).collect();
lines.join("\n")
}
fn indexsubfolders(&self) -> String {
let mut res = vec![];
for fold in &self.subfolders {
let q: PathBuf = (&fold.root).iter().skip(1).collect();
let p = q.into_os_string().into_string().unwrap();
let links: String = (&fold.curentries)
.into_iter()
.map(|x| x.linkline())
.collect::<Vec<String>>()
.join("\n");
if let Some(ind) = &fold.index {
let k: PathBuf = (&ind.dir).iter().skip(1).collect();
let indlink = k.into_os_string().into_string().unwrap();
let indline = format!("=> {} Index of {}", indlink, p);
res.push(format!("### {}\n{}\n\n{}", p, indline, links));
} else {
// println!("branch 2");
res.push(format!("### {}\n\n{}", p, links));
}
}
res.join("\n----------------------------\n")
}
fn refactorindex(&mut self) {
if let Some(ind) = &self.index {
let mut k = ind.clone();
let c1 = k.content.replace("{{ full-list }}", &self.indexallsubs());
let c2 = c1.replace("{{ folders }}", &self.indexsubfolders());
k.content = c2;
self.index = Some(k);
}
for sub in &mut self.subfolders {
sub.refactorindex();
}
}
fn writetodisk(&self) -> io::Result<()> {
create_dir(self.root.clone())?;
if let Some(ind) = &self.index {
write(ind.dir.clone(), ind.content.clone())?;
}
for e in &self.curentries {
copy(e.olddir.clone(), e.dir.clone())?;
}
for ne in &self.curfolder {
copy(ne.olddir.clone(), ne.dir.clone())?;
}
for sub in &self.subfolders {
sub.writetodisk()?
}
Ok(())
}
}
fn visit_dirs(dir: &Path) -> io::Result<Gemdir> {
if dir.is_dir() {
let mut cdir = Gemdir {
root: dir.to_path_buf(),
index: None,
curfolder: vec![],
curentries: vec![],
subfolders: vec![],
};
for entry in fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
println!("found : {:?}", path);
if path.is_dir() {
cdir.subfolders.push(visit_dirs(&path)?);
} else {
let f = path.file_name().expect("File has no name?");
if f == "index.gmi" || f == "index.gemini" {
// println!("filename: {:?}", e);
let i = Index {
dir: path.to_path_buf(),
content: read_to_string(path)?,
};
cdir.index = Some(i);
} else if let Some(e) = path.extension() {
if e == "gmi" || e == "gemini" {
if let Some(e) = parse_entry(&path) {
cdir.curentries.push(e);
} else {
cdir.curfolder.push(Nonentry {
dir: path.to_path_buf(),
olddir: path.to_path_buf(),
})
}
}
}
}
}
cdir.curentries.sort();
return Ok(cdir);
}
Err(Error::from(ErrorKind::NotFound))
}
fn parse_entry(dir: &Path) -> Option<GemEntry> {
let t = dir.file_name().unwrap();
let f = t.to_os_string().into_string().unwrap();
let filename = f.clone();
let (title, rest) = split_once(&filename)?;
// println!("{}", title);
// println!("{:?}", rest);
let date;
if let Ok(d) = NaiveDate::parse_from_str(rest, "%Y-%m-%d.gmi") {
date = d;
} else if let Ok(d) = NaiveDate::parse_from_str(rest, "%Y-%m-%d.gemini") {
date = d;
} else {
return None;
}
let e = GemEntry {
title: title.to_string(),
dir: dir.to_path_buf(),
olddir: dir.to_path_buf(),
date,
};
Some(e)
}
// found on stackoverflow
fn split_once(in_string: &str) -> Option<(&str, &str)> {
let mut splitter = in_string.splitn(2, '-');
let first = splitter.next()?;
let second = splitter.next()?;
Some((first, second))
}
fn writefeed(g: &Gemdir, title: String, url: String, author: String) -> io::Result<()> {
let k = Path::new("atom.xml");
let p = g.root.clone().join(&k);
let mut feed = Feed::default();
feed.set_base(url.clone());
feed.set_title(title);
feed.set_id(url.clone());
let posts = g.getsubfiles();
let lastupdatenaive = posts[0].date;
let lastupdate = getfixeddate(lastupdatenaive);
feed.set_updated(lastupdate);
let mut entries = vec![];
for p in posts {
let mut e = Entry::default();
e.set_updated(getfixeddate(p.date));
e.set_title(p.title.replace("_", " "));
let mut auth = Person::default();
auth.set_name(author.clone());
e.set_authors(vec![auth]);
let url = format!("{}/{}", url.clone(), p.getlink());
let mut link = Link::default();
link.set_href(url.clone());
e.set_links(vec![link]);
e.set_id(url);
entries.push(e);
}
feed.set_entries(entries);
write(p, feed.to_string())
}
fn getfixeddate(d: NaiveDate) -> DateTime<FixedOffset> {
let naivedatetime = d.and_hms(0, 0, 0);
let curtime = Local::now();
let offset = curtime.offset();
offset.from_local_datetime(&naivedatetime).unwrap()
}
fn buildsite(
dir: &Path,
g: &mut Gemdir,
title: String,
url: String,
author: String,
) -> io::Result<()> {
g.changeroot(dir);
g.refactorindex();
g.writetodisk()?;
writefeed(g, title, url, author)
}
fn main() {
let args: Vec<String> = env::args().collect();
// let dir = Path::new("/home/raphy/Documents/Projects/Rust/rugem/testsite/");
let dir = Path::new(&args[1]);
let dst = Path::new(&args[2]);
let title = &args[3];
let url = &args[4];
let auth = &args[5];
let mut k = visit_dirs(&dir).unwrap();
buildsite(
dst,
&mut k,
title.to_string(),
url.to_string(),
auth.to_string(),
);
// print!("{:?}", k)
}

3
todo Normal file
View File

@ -0,0 +1,3 @@
- Fix relative / absolute urls
- Proper module segmentation
- Output to HTML...