-use crate::template::{find, parse, TemplateContext};
+use crate::template::{find, parse, Context};
use std::fs::write;
use std::io::{Error, ErrorKind::Other, Result};
use std::path::Path;
_: &Path,
template_directory: &Path,
target: &Path,
- context: &TemplateContext,
+ context: &Context,
) -> Result<()> {
if let Some(template) = find(template_directory, FILENAME) {
let parsed_template = parse(&template)
-use crate::template::{find, parse, TemplateContext};
+use crate::template::{find, parse, Context};
use std::fs::write;
use std::io::{Error, ErrorKind::Other, Result};
use std::path::Path;
_: &Path,
template_directory: &Path,
target: &Path,
- context: &TemplateContext,
+ context: &Context,
) -> Result<()> {
if let Some(template) = find(template_directory, FILENAME) {
let parsed_template = parse(&template)
mod gopher;
mod raw;
-use crate::template::{TemplateContext, TemplateValue};
+use crate::template::{Context, Value};
use std::collections::HashMap;
use std::fs::read_dir;
use std::io::Result;
slug: String,
}
-type Archiver = fn(&Path, &Path, &Path, &TemplateContext) -> Result<()>;
+type Archiver = fn(&Path, &Path, &Path, &Context) -> Result<()>;
impl ArchiveEntry {
- pub fn to_template_context(archive_entries: &[ArchiveEntry]) -> TemplateContext {
+ pub fn to_template_context(archive_entries: &[ArchiveEntry]) -> Context {
let mut context = HashMap::new();
let archive_entries_collection = archive_entries
context.insert(
"archive_length".to_string(),
- TemplateValue::Unsigned(archive_entries.len().try_into().unwrap()),
+ Value::Unsigned(archive_entries.len().try_into().unwrap()),
);
context.insert(
"posts".to_string(),
- TemplateValue::Collection(archive_entries_collection),
+ Value::Collection(archive_entries_collection),
);
context
}
- pub fn to_template_value(&self) -> TemplateContext {
+ pub fn to_template_value(&self) -> Context {
let mut context = HashMap::new();
- context.insert("id".to_string(), TemplateValue::String(self.id.clone()));
+ context.insert("id".to_string(), Value::String(self.id.clone()));
- context.insert("slug".to_string(), TemplateValue::String(self.slug.clone()));
+ context.insert("slug".to_string(), Value::String(self.slug.clone()));
if let Some(title) = self.title() {
- context.insert("title".to_string(), TemplateValue::String(title));
+ context.insert("title".to_string(), Value::String(title));
}
context
-use crate::template::TemplateContext;
+use crate::template::Context;
use crate::utils::recursively_copy;
use std::io::Result;
use std::path::Path;
-pub fn archive(
- archive_directory: &Path,
- _: &Path,
- target: &Path,
- _: &TemplateContext,
-) -> Result<()> {
+pub fn archive(archive_directory: &Path, _: &Path, target: &Path, _: &Context) -> Result<()> {
if archive_directory.exists() {
return recursively_copy(archive_directory, target);
}
Generate
}
- fn read_posts(&self, posts_directory: &Path, max_posts: u8) -> Vec<Post> {
+ fn read_posts(posts_directory: &Path, max_posts: u8) -> Vec<Post> {
let mut posts = Vec::new();
for i in 0..max_posts {
let post_path = posts_directory.join(i.to_string());
- match self.read_post(&post_path, i) {
+ match Generate::read_post(&post_path, i) {
Some(post) => posts.push(post),
None => continue,
}
None
}
- fn read_post(&self, post_directory: &Path, index: u8) -> Option<Post> {
+ fn read_post(post_directory: &Path, index: u8) -> Option<Post> {
let metadata_path = post_directory.join(METADATA_FILENAME);
let metadata = Metadata::read_or_create(&metadata_path);
let raw = Generate::find_blog_content(post_directory)?;
let _ = remove_dir_all(&configuration.blog_output_directory);
create_dir_all(&configuration.blog_output_directory)?;
- let posts = self.read_posts(&configuration.posts_directory, configuration.max_posts);
+ let posts = Generate::read_posts(&configuration.posts_directory, configuration.max_posts);
generate(
&configuration.static_directory,
&configuration.templates_directory,
Ok(directory) => PathBuf::from(directory),
Err(_) => match env::var(default_value) {
Ok(directory) => PathBuf::from(directory),
- Err(_) => match env::var("HOME") {
- Ok(directory) => PathBuf::from(directory).join(home_fallback),
- Err(_) => panic!(
- "Could not find required directory, {} or {} should be set and readable.",
- user_override, default_value
- ),
- },
+ Err(_) => env::var("HOME")
+ .map_or_else(
+ |_| panic!("Could not find required directory, {user_override} or {default_value} should be set and readable"),
+ |directory| PathBuf::from(directory).join(home_fallback)
+ )
},
}
.join(path)
-use crate::template::{find, parse, TemplateContext};
+use crate::template::{find, parse, Context};
use std::fs::write;
use std::io::{Error, ErrorKind::Other, Result};
-use std::path::PathBuf;
+use std::path::Path;
const FILENAME: &str = "index.html";
pub fn generate(
- _: &PathBuf,
- template_directory: &PathBuf,
- target: &PathBuf,
- context: &TemplateContext,
+ _: &Path,
+ template_directory: &Path,
+ target: &Path,
+ context: &Context,
) -> Result<()> {
- match find(template_directory, FILENAME) {
- Some(template) => {
- let parsed_template = parse(&template)
- .ok_or_else(|| Error::new(Other, "Unable to parse HTML template"))?;
- let rendered_template = parsed_template.render(context)?;
- let location = target.join(FILENAME);
- write(location, rendered_template)?;
- }
- None => {}
+ if let Some(template) = find(template_directory, FILENAME) {
+ let parsed_template =
+ parse(&template).ok_or_else(|| Error::new(Other, "Unable to parse HTML template"))?;
+ let rendered_template = parsed_template.render(context)?;
+ let location = target.join(FILENAME);
+ write(location, rendered_template)?;
}
Ok(())
}
mod txt;
use crate::post::Post;
-use crate::template::TemplateContext;
+use crate::template::Context;
use std::io::Result;
-use std::path::PathBuf;
+use std::path::Path;
+
+type Generator = fn(&Path, &Path, &Path, &Context) -> Result<()>;
pub fn generate(
- static_directory: &PathBuf,
- template_directory: &PathBuf,
- output_directory: &PathBuf,
- posts: &Vec<Post>,
+ static_directory: &Path,
+ template_directory: &Path,
+ output_directory: &Path,
+ posts: &[Post],
) -> Result<()> {
let generators = available_generators();
- let context = Post::to_template_context(&posts);
+ let context = Post::to_template_context(posts);
for generator in generators {
generator(
static_directory,
Ok(())
}
-fn available_generators() -> Vec<fn(&PathBuf, &PathBuf, &PathBuf, &TemplateContext) -> Result<()>> {
+fn available_generators() -> Vec<Generator> {
vec![
static_files::generate,
// These three are actually the same. Can generalize, don't know how in rust yet.
-use crate::template::{find, parse, TemplateContext};
+use crate::template::{find, parse, Context};
use std::fs::write;
use std::io::{Error, ErrorKind::Other, Result};
-use std::path::PathBuf;
+use std::path::Path;
const FILENAME: &str = "feed.xml";
pub fn generate(
- _: &PathBuf,
- template_directory: &PathBuf,
- target: &PathBuf,
- context: &TemplateContext,
+ _: &Path,
+ template_directory: &Path,
+ target: &Path,
+ context: &Context,
) -> Result<()> {
- match find(template_directory, FILENAME) {
- Some(template) => {
- let parsed_template = parse(&template)
- .ok_or_else(|| Error::new(Other, "Unable to parse RSS template"))?;
- let rendered_template = parsed_template.render(context)?;
- let location = target.join(FILENAME);
- write(location, rendered_template)?;
- }
- None => {}
+ if let Some(template) = find(template_directory, FILENAME) {
+ let parsed_template =
+ parse(&template).ok_or_else(|| Error::new(Other, "Unable to parse RSS template"))?;
+ let rendered_template = parsed_template.render(context)?;
+ let location = target.join(FILENAME);
+ write(location, rendered_template)?;
}
Ok(())
}
-use crate::template::TemplateContext;
+use crate::template::Context;
use crate::utils::recursively_copy;
use std::io::Result;
-use std::path::PathBuf;
+use std::path::Path;
-pub fn generate(
- source: &PathBuf,
- _: &PathBuf,
- target: &PathBuf,
- _: &TemplateContext,
-) -> Result<()> {
+pub fn generate(source: &Path, _: &Path, target: &Path, _: &Context) -> Result<()> {
if source.exists() {
return recursively_copy(source, target);
}
-use crate::template::{find, parse, TemplateContext};
+use crate::template::{find, parse, Context};
use std::fs::write;
use std::io::{Error, ErrorKind::Other, Result};
-use std::path::PathBuf;
+use std::path::Path;
const FILENAME: &str = "index.txt";
pub fn generate(
- _: &PathBuf,
- template_directory: &PathBuf,
- target: &PathBuf,
- context: &TemplateContext,
+ _: &Path,
+ template_directory: &Path,
+ target: &Path,
+ context: &Context,
) -> Result<()> {
- match find(template_directory, FILENAME) {
- Some(template) => {
- let parsed_template = parse(&template)
- .ok_or_else(|| Error::new(Other, "Unable to parse TXT template"))?;
- let rendered_template = parsed_template.render(context)?;
- let location = target.join(FILENAME);
- write(location, rendered_template)?;
- }
- None => {}
+ if let Some(template) = find(template_directory, FILENAME) {
+ let parsed_template =
+ parse(&template).ok_or_else(|| Error::new(Other, "Unable to parse TXT template"))?;
+ let rendered_template = parsed_template.render(context)?;
+ let location = target.join(FILENAME);
+ write(location, rendered_template)?;
}
Ok(())
}
result
} else {
match result {
- Ok(_) => Ok(()),
+ Ok(()) => Ok(()),
Err(e) => {
- eprintln!("{}", e);
+ eprintln!("{e}");
std::process::exit(1);
}
}
let command_chain: Vec<Box<dyn Command>> = before_commands
.into_iter()
.chain(once(main_command))
- .chain(after_commands.into_iter())
+ .chain(after_commands)
.collect();
for command in command_chain {
- let result = command.execute(arguments.get(2), &configuration, command_name);
- if let Err(_) = result {
- return result;
- }
+ command.execute(arguments.get(2), &configuration, command_name)?;
}
return Ok(());
}
}
- Help::new().execute(None, &configuration, &"help".to_string())
+ Help::new().execute(None, &configuration, "help")
}
impl Metadata {
pub fn read_or_create(file_path: &PathBuf) -> Metadata {
- match Metadata::read_metadata_file(file_path) {
- Some(metadata) => metadata,
- None => {
- let timestamp = SystemTime::now()
- .duration_since(UNIX_EPOCH)
- .map(|duration| duration.as_millis() as u64)
- .unwrap_or_else(|_| 0);
- return Metadata {
- id: timestamp.to_string(),
- created_on: timestamp,
- };
+ if let Some(metadata) = Metadata::read_metadata_file(file_path) {
+ metadata
+ } else {
+ let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).map_or_else(
+ |_| 0,
+ |duration| u64::try_from(duration.as_millis()).expect("Timestamp is too big!"),
+ );
+ Metadata {
+ id: timestamp.to_string(),
+ created_on: timestamp,
}
}
}
pub fn created_on_utc(&self) -> Option<String> {
let date =
OffsetDateTime::from_unix_timestamp_nanos((self.created_on * 1_000_000).into()).ok()?;
- return date.format(&Rfc2822).ok();
+ date.format(&Rfc2822).ok()
}
fn read_metadata_file(file_path: &PathBuf) -> Option<Metadata> {
use crate::metadata::Metadata;
-use crate::template::{TemplateContext, TemplateValue};
+use crate::template::{Context, Value};
use std::collections::HashMap;
pub struct Post {
}
impl Post {
- pub fn to_template_context(posts: &Vec<Post>) -> TemplateContext {
+ pub fn to_template_context(posts: &[Post]) -> Context {
let mut context = HashMap::new();
- let posts_collection = posts.iter().map(|post| post.to_template_value()).collect();
- context.insert(
- "has_posts".to_string(),
- TemplateValue::Bool(posts.len() > 0),
- );
+ let posts_collection = posts.iter().map(Post::to_template_value).collect();
+ context.insert("has_posts".to_string(), Value::Bool(!posts.is_empty()));
context.insert(
"posts_length".to_string(),
- TemplateValue::Unsigned(posts.len().try_into().unwrap()),
- );
- context.insert(
- "posts".to_string(),
- TemplateValue::Collection(posts_collection),
+ Value::Unsigned(posts.len().try_into().unwrap()),
);
+ context.insert("posts".to_string(), Value::Collection(posts_collection));
context
}
- pub fn to_template_value(&self) -> TemplateContext {
+ pub fn to_template_value(&self) -> Context {
let mut context = HashMap::new();
context.insert(
"id".to_string(),
- TemplateValue::String(format!("{}", self.metadata.id)),
+ Value::String(self.metadata.id.to_string()),
);
context.insert(
"created_on".to_string(),
- TemplateValue::Unsigned(self.metadata.created_on),
+ Value::Unsigned(self.metadata.created_on),
);
if let Some(created_on_utc) = self.metadata.created_on_utc() {
- context.insert(
- "created_on_utc".to_string(),
- TemplateValue::String(created_on_utc),
- );
+ context.insert("created_on_utc".to_string(), Value::String(created_on_utc));
}
- context.insert("title".to_string(), TemplateValue::String(self.title()));
- context.insert(
- "index".to_string(),
- TemplateValue::Unsigned(self.index.into()),
- );
- context.insert(
- "html".to_string(),
- TemplateValue::String(format!("{}", self.html)),
- );
+ context.insert("title".to_string(), Value::String(self.title()));
+ context.insert("index".to_string(), Value::Unsigned(self.index.into()));
+ context.insert("html".to_string(), Value::String(self.html.to_string()));
context.insert(
"escaped_html".to_string(),
- TemplateValue::String(format!("{}", self.escaped_html())),
- );
- context.insert(
- "raw".to_string(),
- TemplateValue::String(format!("{}", self.raw)),
+ Value::String(self.escaped_html().to_string()),
);
+ context.insert("raw".to_string(), Value::String(self.raw.to_string()));
context
}
fn title(&self) -> String {
self.raw
- .trim()
- .split('\n')
+ .lines()
.next()
.unwrap()
.replace('#', "")
- .replace("&", "&")
+ .replace('&', "&")
.trim()
.to_string()
}
fn escaped_html(&self) -> String {
self.html
- .replace("&", "&")
- .replace("<", "<")
- .replace(">", ">")
- .replace("\"", """)
- .replace("'", "'")
+ .replace('&', "&")
+ .replace('<', "<")
+ .replace('>', ">")
+ .replace('"', """)
+ .replace('\'', "'")
}
}
use std::io::{Error, ErrorKind::Other, Result};
-use std::path::PathBuf;
+use std::path::Path;
use std::process::{Command, Stdio};
use std::time::{SystemTime, UNIX_EPOCH};
true
}
- fn sync_up(&self, remote: &str, directory: &PathBuf) -> Result<()> {
+ fn sync_up(&self, remote: &str, directory: &Path) -> Result<()> {
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_err(|_| Error::new(Other, "Invalid time"))?
Ok(())
}
- fn sync_down(&self, remote: &str, directory: &PathBuf) -> Result<()> {
+ fn sync_down(&self, remote: &str, directory: &Path) -> Result<()> {
let commands = vec![
format!("cd {} && git init -b main", directory.display()),
format!("cd {} && git checkout .", directory.display()),
use std::fs::{create_dir_all, remove_file, write, File};
use std::io::{Error, ErrorKind::Other, Read, Result};
-use std::path::PathBuf;
+use std::path::Path;
use git::Git;
pub trait Remote {
fn can_handle(&self, remote: &str) -> bool;
- fn sync_up(&self, remote: &str, directory: &PathBuf) -> Result<()>;
- fn sync_down(&self, remote: &str, directory: &PathBuf) -> Result<()>;
+ fn sync_up(&self, remote: &str, directory: &Path) -> Result<()>;
+ fn sync_down(&self, remote: &str, directory: &Path) -> Result<()>;
}
-pub fn add(config_directory: &PathBuf, remote_config: &PathBuf, remote: &String) -> Result<()> {
+pub fn add(config_directory: &Path, remote_config: &Path, remote: &String) -> Result<()> {
create_dir_all(config_directory)?;
write(remote_config, remote)?;
Ok(())
}
-pub fn remove(remote_config: &PathBuf) -> Result<()> {
+pub fn remove(remote_config: &Path) -> Result<()> {
if remote_config.exists() {
- remove_file(remote_config)?
+ remove_file(remote_config)?;
}
Ok(())
}
-pub fn sync_up(data_directory: &PathBuf, remote_config: &PathBuf) -> Result<()> {
+pub fn sync_up(data_directory: &Path, remote_config: &Path) -> Result<()> {
let remote_address =
read_remote(remote_config).ok_or_else(|| Error::new(Other, "No remote is configured"))?;
create_dir_all(data_directory)?;
))
}
-pub fn sync_down(data_directory: &PathBuf, remote_config: &PathBuf) -> Result<()> {
+pub fn sync_down(data_directory: &Path, remote_config: &Path) -> Result<()> {
let remote_address =
read_remote(remote_config).ok_or_else(|| Error::new(Other, "No remote is configured"))?;
create_dir_all(data_directory)?;
vec![Box::new(Git::new())]
}
-fn read_remote(file_path: &PathBuf) -> Option<String> {
+fn read_remote(file_path: &Path) -> Option<String> {
let mut file = File::open(file_path).ok()?;
let mut contents = String::new();
file.read_to_string(&mut contents).ok()?;
impl std::fmt::Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
- Token::Text(label) => write!(f, "Text {}", label),
- Token::DisplayDirective { content } => write!(f, "DisplayDirective {}", content),
+ Token::Text(label) => write!(f, "Text {label}"),
+ Token::DisplayDirective { content } => write!(f, "DisplayDirective {content}"),
Token::ConditionalDirective {
condition,
children,
} => {
- write!(f, "ConditionalDirective {} [[[\n", condition)?;
+ writeln!(f, "ConditionalDirective {condition} [[[")?;
for child in children {
- write!(f, "\t{}\n", child)?;
+ writeln!(f, "\t{child}")?;
}
write!(f, "\n]]]")
}
member_label,
children,
} => {
- write!(f, "{} in {}\n", collection, member_label)?;
+ writeln!(f, "{collection} in {member_label}")?;
for child in children {
- write!(f, "\t{}\n", child)?;
+ writeln!(f, "\t{child}")?;
}
write!(f, "\n]]]")
}
}
#[derive(Clone)]
-pub enum TemplateValue {
+pub enum Value {
String(String),
Unsigned(u64),
Bool(bool),
- Collection(Vec<TemplateContext>),
- Context(TemplateContext),
+ Collection(Vec<Context>),
+ Context(Context),
}
-impl TemplateValue {
+impl Value {
fn render(&self) -> String {
match self {
- TemplateValue::String(string) => string.to_string(),
- TemplateValue::Unsigned(number) => format!("{}", number),
- TemplateValue::Bool(bool) => format!("{}", bool),
- _ => "".to_string(),
+ Value::String(string) => string.to_string(),
+ Value::Unsigned(number) => format!("{number}"),
+ Value::Bool(bool) => format!("{bool}"),
+ _ => String::new(),
}
}
}
-pub type TemplateContext = HashMap<String, TemplateValue>;
+pub type Context = HashMap<String, Value>;
-struct TemplateContextGetter {}
-impl TemplateContextGetter {
- fn get(context: &TemplateContext, path: &str) -> Option<TemplateValue> {
+struct ContextGetter {}
+impl ContextGetter {
+ fn get(context: &Context, path: &str) -> Option<Value> {
let path_parts: Vec<&str> = path.split('.').collect();
- TemplateContextGetter::recursively_get_value(context, &path_parts)
+ ContextGetter::recursively_get_value(context, &path_parts)
}
- fn recursively_get_value(context: &TemplateContext, path: &[&str]) -> Option<TemplateValue> {
+ fn recursively_get_value(context: &Context, path: &[&str]) -> Option<Value> {
match context.get(path[0]) {
- Some(TemplateValue::Context(next)) if path.len() > 1 => {
- TemplateContextGetter::recursively_get_value(next, &path[1..])
+ Some(Value::Context(next)) if path.len() > 1 => {
+ ContextGetter::recursively_get_value(next, &path[1..])
}
Some(value) if path.len() == 1 => Some(value.clone()),
_ => None,
}
}
-pub struct ParsedTemplate {
+pub struct Parsed {
pub tokens: Vec<Token>,
}
-impl ParsedTemplate {
- pub fn render(&self, context: &TemplateContext) -> Result<String> {
- ParsedTemplate::render_tokens(&self.tokens, context)
+impl Parsed {
+ pub fn render(&self, context: &Context) -> Result<String> {
+ Parsed::render_tokens(&self.tokens, context)
}
- pub fn render_tokens(tokens: &Vec<Token>, context: &TemplateContext) -> Result<String> {
+ pub fn render_tokens(tokens: &Vec<Token>, context: &Context) -> Result<String> {
let mut rendered_template: String = String::new();
for token in tokens {
match token {
- Token::Text(contents) => rendered_template.push_str(&contents),
+ Token::Text(contents) => rendered_template.push_str(contents),
Token::DisplayDirective { content } => {
- let value = TemplateContextGetter::get(context, &content).ok_or_else(|| {
- Error::new(Other, format!("{} is not a valid key", content))
+ let value = ContextGetter::get(context, content).ok_or_else(|| {
+ Error::new(Other, format!("{content} is not a valid key"))
})?;
rendered_template.push_str(&value.render());
}
condition = condition[1..].to_string();
}
- let value =
- TemplateContextGetter::get(context, &condition).ok_or_else(|| {
- Error::new(Other, format!("{} is not a valid key", condition))
- })?;
+ let value = ContextGetter::get(context, &condition).ok_or_else(|| {
+ Error::new(Other, format!("{condition} is not a valid key"))
+ })?;
match value {
- TemplateValue::Bool(value) => {
+ Value::Bool(value) => {
if negator ^ value {
rendered_template
- .push_str(&ParsedTemplate::render_tokens(children, context)?)
+ .push_str(&Parsed::render_tokens(children, context)?);
}
Ok(())
}
_ => Err(Error::new(
Other,
- format!("{} is not a boolean value", condition),
+ format!("{condition} is not a boolean value"),
)),
}?;
}
member_label,
children,
} => {
- let value =
- TemplateContextGetter::get(context, &collection).ok_or_else(|| {
- Error::new(Other, format!("{} is not a valid key", collection))
- })?;
+ let value = ContextGetter::get(context, collection).ok_or_else(|| {
+ Error::new(Other, format!("{collection} is not a valid key"))
+ })?;
match value {
- TemplateValue::Collection(collection) => {
+ Value::Collection(collection) => {
for member in collection {
let mut child_context = context.clone();
- child_context.insert(
- member_label.to_string(),
- TemplateValue::Context(member),
- );
- rendered_template.push_str(&ParsedTemplate::render_tokens(
- &children,
- &child_context,
- )?)
+ child_context
+ .insert(member_label.to_string(), Value::Context(member));
+ rendered_template
+ .push_str(&Parsed::render_tokens(children, &child_context)?);
}
Ok(())
}
_ => Err(Error::new(
Other,
- format!("{} is not a collection", collection),
+ format!("{collection} is not a collection"),
)),
}?;
}
}
}
-pub fn parse(template: &str) -> Option<ParsedTemplate> {
+pub fn parse(template: &str) -> Option<Parsed> {
let mut tokens = Vec::new();
tokenize(template, &mut tokens).ok()?;
- Some(ParsedTemplate { tokens })
+ Some(Parsed { tokens })
}
fn tokenize(template: &str, tokens: &mut Vec<Token>) -> Result<()> {