Tholp's bespoke website generator

restructure for project parsing and file index (idk if it works lol)

Tholp1 55632975 496c889b

+3
Cargo.toml
···
edition = "2021"
[dependencies]
+
glob = "0.3.2"
markdown = "1.0.0-alpha.21"
+
serde = "1.0.218"
+
toml = "0.8.19"
+7 -1
src/macros/clear.rs
···
use crate::{
+
projectparse::ProjectContext,
stringtools::{split_keep_delimiters, strings_to_tokens},
types::{InputFile, Token},
};
-
pub fn macro_clear(_file: &mut InputFile, _args: &Vec<String>, _scope: &[Token]) -> Vec<Token> {
+
pub fn macro_clear(
+
_file: &mut InputFile,
+
_context: &mut ProjectContext,
+
_args: &Vec<String>,
+
_scope: &[Token],
+
) -> Vec<Token> {
_file.tokens = _file.tokens.split_off(_file.working_index);
_file.working_index = 0;
return Vec::new();
+12 -3
src/macros/insert.rs
···
-
use std::{env::Args, fs};
+
use std::{env::Args, fs, path::PathBuf};
use crate::{
+
projectparse::{FileIndexing, ProjectContext},
stringtools::{split_keep_delimiters, strings_to_tokens},
types::{InputFile, Token},
};
-
pub fn macro_insert(_file: &mut InputFile, args: &Vec<String>, _scope: &[Token]) -> Vec<Token> {
+
pub fn macro_insert(
+
_file: &mut InputFile,
+
_context: &mut ProjectContext,
+
args: &Vec<String>,
+
_scope: &[Token],
+
) -> Vec<Token> {
print!("\nargs: {:?}\n", args);
let mut output = fs::read_to_string(args[0].clone()).expect("File unreadable or missing");
if output.ends_with("\n") {
···
} //remove trailing newline
let split_output = split_keep_delimiters(output);
-
return strings_to_tokens(split_output, args[0].clone());
+
return strings_to_tokens(
+
split_output,
+
_context.index_of_file(&PathBuf::from(&args[0])),
+
);
}
+3 -3
src/macros/mod.rs
···
has_scope: true,
},
Macro {
-
symbol: "repeat", // Outputs what its give x number of times
+
symbol: "repeat", // Outputs what its give x number of times
expand: macro_repeat,
-
has_scope: true
-
}
+
has_scope: true,
+
},
];
+25 -10
src/macros/simple_blocks.rs
···
-
use crate::types::{InputFile, Token};
+
use crate::{
+
projectparse::ProjectContext,
+
types::{InputFile, Token},
+
};
-
pub fn macro_comment(_file: &mut InputFile, _args: &Vec<String>, _scope: &[Token]) -> Vec<Token> {
+
pub fn macro_comment(
+
_file: &mut InputFile,
+
_context: &mut ProjectContext,
+
_args: &Vec<String>,
+
_scope: &[Token],
+
) -> Vec<Token> {
return Vec::new();
}
-
pub fn macro_null(_file: &mut InputFile, _args: &Vec<String>, scope: &[Token]) -> Vec<Token> {
+
pub fn macro_null(
+
_file: &mut InputFile,
+
_context: &mut ProjectContext,
+
_args: &Vec<String>,
+
scope: &[Token],
+
) -> Vec<Token> {
let mut tokens = Vec::new();
for tok in scope {
tokens.push(tok.clone());
···
return tokens;
}
-
pub fn macro_repeat(_file: &mut InputFile, args: &Vec<String>, scope: &[Token]) -> Vec<Token> {
+
pub fn macro_repeat(
+
_file: &mut InputFile,
+
_context: &mut ProjectContext,
+
args: &Vec<String>,
+
scope: &[Token],
+
) -> Vec<Token> {
let mut count = 0;
-
if args.len() > 0
-
{
+
if args.len() > 0 {
count = args[0].parse().unwrap_or(0);
}
let mut tokens = Vec::new();
-
for _i in 0..count
-
{
-
for tok in scope
-
{
+
for _i in 0..count {
+
for tok in scope {
tokens.push(tok.clone());
}
}
+23 -23
src/main.rs
···
mod macros;
+
mod projectparse;
mod stringtools;
mod types;
use macros::MACRO_LIST;
use markdown::{to_html_with_options, CompileOptions, Options};
+
use projectparse::{parse_project, FileIndexing, ProjectContext};
use std::{
env,
fs::{self, File},
···
static DELIMITERS: [char; 10] = [' ', '\n', '\t', '(', ')', '{', '}', '\\', '\'', '\"'];
fn main() {
-
let mut files: Vec<types::InputFile> = Vec::new();
-
let mut args: Vec<String> = env::args().collect();
-
args.remove(0);
-
-
for file in args.iter() {
-
let mut new_file = types::InputFile::new();
-
new_file.filename_input = file.to_string();
-
new_file.filename_skidout = file.to_string() + ".skidout";
-
new_file.filename_htmlout = file.to_string() + ".html";
-
files.push(new_file);
-
}
-
println!("{:?}", args);
-
for f in &mut files {
-
process_file(f);
+
let mut project = parse_project(env::current_dir().unwrap().as_path());
+
for group in &mut project.filegroups {
+
for infile in &mut group.files {
+
process_file(infile, &mut project.context);
+
}
}
}
-
fn process_file(file: &mut InputFile) {
-
let contents = fs::read_to_string(&file.filename_input).expect("File unreadable or missing");
+
fn process_file(file: &mut InputFile, context: &mut ProjectContext) {
+
//}, context: &mut ProjectContext) {
+
let contents = fs::read_to_string(&file.file_input).expect("File unreadable or missing");
//println!("{}\n {}", f.filename_out, contents);
//file.tokens = strings_to_tokens(split_keep_delimiters(contents), file.filename_input.clone());
-
file.tokens = split_to_tokens(contents, file.filename_input.clone());
+
file.tokens = split_to_tokens(contents, context.index_of_file(&file.file_input));
//let mut escaped = false;
while file.working_index < file.tokens.len() {
···
if symbol.len() > 2 {
let mut ephemeral = false;
-
let same_file = file.tokens[file.working_index].origin_file != file.filename_input;
+
let same_file = file.tokens[file.working_index].origin_file
+
!= context.index_of_file(&file.file_input);
// Inversely Ephemeral
if symbol.starts_with("!&") {
···
&file.tokens[(file.working_index + args_tokcount)..],
);
println!("{}", block_tokcount);
-
expansion = (m.expand)(file, &args, &block[..]);
+
expansion = (m.expand)(file, context, &args, &block[..]);
} else {
block_tokcount = 0;
-
expansion = (m.expand)(file, &args, &Vec::new()[..]);
+
expansion = (m.expand)(file, context, &args, &Vec::new()[..]);
}
}
···
for t in &file.tokens {
skid_output += &t.contents;
}
-
fs::write(&file.filename_skidout, &skid_output).expect("Couldn't write skid to file");
+
fs::write(&file.file_skidout, &skid_output).expect("Couldn't write skid to file");
//let html_output = markdown::to_html(&skid_output);
let html_output = markdown::to_html_with_options(
···
},
)
.unwrap();
-
fs::write(&file.filename_htmlout, &html_output).expect("Couldn't write html to file");
-
println!("{} written.", file.filename_htmlout);
+
fs::write(&file.file_htmlout, &html_output).expect("Couldn't write html to file");
+
println!(
+
"{} written.",
+
file.file_htmlout
+
.to_str()
+
.unwrap_or("Couldnt Unwrap htmlout name")
+
);
}
+183
src/projectparse.rs
···
+
use crate::types::{self, InputFile};
+
use std::{
+
any::Any,
+
fs,
+
iter::{FilterMap, Map},
+
os::unix::process,
+
path::{Path, PathBuf},
+
string,
+
};
+
use toml::{ser, Table};
+
+
pub struct Project {
+
pub filegroups: Vec<FileGroup>,
+
pub settings: ProjectSettings,
+
pub context: ProjectContext,
+
}
+
+
pub struct FileGroup {
+
pub name: String,
+
pub files: Vec<InputFile>,
+
pub pre_insert: PathBuf,
+
pub post_insert: PathBuf,
+
pub process: bool,
+
}
+
+
pub struct ProjectSettings {
+
pub input_folder: PathBuf,
+
pub output_folder: PathBuf,
+
pub global_pre_insert: PathBuf,
+
pub global_post_insert: PathBuf,
+
}
+
+
pub struct ProjectContext {
+
pub filemap: Vec<PathBuf>, // mapped to index
+
}
+
+
macro_rules! get_table_bool_or_default {
+
($table:ident, $key:expr, $default:expr) => {
+
$table
+
.get($key)
+
.unwrap_or(&toml::Value::try_from($default).unwrap())
+
.as_bool()
+
.unwrap_or($default)
+
};
+
}
+
+
macro_rules! get_table_string_or_default {
+
($table:ident, $key:expr, $default:expr) => {
+
// $table
+
// .get($key)
+
// .unwrap_or(&toml::Value::try_from($default).unwrap())
+
// .as_str()
+
// .unwrap_or($default)
+
if $table.contains_key($key) {
+
$table.get($key).unwrap().as_str().unwrap()
+
} else {
+
$default
+
}
+
};
+
}
+
+
pub fn parse_project(tomlpath: &Path) -> Project {
+
let tomlfile = fs::read_to_string(tomlpath).expect("Project file unreadable or missing.");
+
+
let mut project: Project = Project {
+
filegroups: Vec::new(),
+
settings: ProjectSettings {
+
input_folder: PathBuf::new(),
+
output_folder: PathBuf::new(),
+
global_pre_insert: PathBuf::new(),
+
global_post_insert: PathBuf::new(),
+
},
+
context: ProjectContext {
+
filemap: Vec::new(),
+
},
+
};
+
let config = tomlfile
+
.parse::<Table>()
+
.expect("Project file not in propper toml format");
+
let settings_section = config["settings"]
+
.as_table()
+
.expect("Project file missing [settings] section");
+
let filegroups_section = config["fileGroups"]
+
.as_table()
+
.expect("Project file contains no file groups ");
+
+
let project_root = tomlpath
+
.parent()
+
.expect("Project file unreadable or missing.");
+
+
project.settings.input_folder = PathBuf::from(get_table_string_or_default!(
+
settings_section,
+
"inputFolder",
+
"skid"
+
));
+
+
project.settings.output_folder = PathBuf::from(get_table_string_or_default!(
+
settings_section,
+
"outputFolder",
+
"content"
+
));
+
+
project.settings.global_pre_insert = project_root.join(get_table_string_or_default!(
+
settings_section,
+
"preInsertGlobal",
+
""
+
));
+
project.settings.global_post_insert = project_root.join(get_table_string_or_default!(
+
settings_section,
+
"postInsertGlobal",
+
""
+
));
+
+
for (k, v) in filegroups_section {
+
if !v.is_table() {
+
continue;
+
}
+
let filegroup_def: &toml::map::Map<String, toml::Value> = v.as_table().unwrap();
+
let name = k.clone();
+
let pre_insert = get_table_string_or_default!(filegroup_def, "preInsert", "");
+
let post_insert = get_table_string_or_default!(filegroup_def, "postInsert", "");
+
let process = get_table_bool_or_default!(filegroup_def, "process", false);
+
+
let recurse_find = get_table_bool_or_default!(filegroup_def, "recursiveFind", false);
+
+
let dir = get_table_string_or_default!(filegroup_def, "folder", "");
+
+
let mut group = FileGroup {
+
files: Vec::new(),
+
name: k.clone(),
+
pre_insert: pre_insert.into(),
+
post_insert: post_insert.into(),
+
process,
+
};
+
+
if filegroup_def.contains_key("files") {
+
let file_array = filegroup_def["files"].as_array().unwrap_or_else(|| {
+
panic!("'files' section of fileGroup.{} needs to be an array", k)
+
});
+
for file in file_array {
+
let filename = file.as_str().unwrap_or_else(|| {
+
panic!(
+
"'files' section of fileGroup.{} needs to only contain strings",
+
k
+
)
+
});
+
let mut new_file = crate::types::InputFile::new();
+
new_file.file_input = project.settings.input_folder.clone();
+
new_file.file_input.push(filename);
+
group.files.push(new_file);
+
}
+
}
+
}
+
+
return project;
+
}
+
+
pub trait FileIndexing {
+
fn index_of_file(&mut self, f: &PathBuf) -> usize;
+
fn file_for_index(&self, i: usize) -> Option<&PathBuf>;
+
}
+
+
impl FileIndexing for ProjectContext {
+
fn index_of_file(&mut self, f: &PathBuf) -> usize {
+
let mut cannonical = f.canonicalize().unwrap();
+
let mut index = 0;
+
for p in &self.filemap {
+
if cannonical == *p {
+
return index;
+
}
+
index = index + 1;
+
}
+
self.filemap.push(cannonical);
+
self.filemap.len()
+
}
+
+
fn file_for_index(&self, i: usize) -> Option<&PathBuf> {
+
if i >= self.filemap.len() {
+
return None;
+
}
+
return Some(&self.filemap[i]);
+
}
+
}
+40 -55
src/stringtools.rs
···
use core::fmt;
-
use std::{
-
ascii::escape_default, f32::consts::E, fmt::Arguments, ops::Index, process::exit, thread::sleep,
-
};
+
use std::{ascii::escape_default, fmt::Arguments, ops::Index, process::exit, thread::sleep};
use super::DELIMITERS;
use crate::types::Token;
···
pub fn collect_arguments(tokens: &[Token]) -> (Vec<String>, usize) {
// Arguments vec and number of tokens consumed
//let mut output = Vec::new();
-
// let mut split_tokens = Vec::new();
-
// for tok in tokens {
-
// for s in split_keep_delimiters(tok.contents.clone()) {
-
// split_tokens.push(s);
-
// }
-
//}
+
let mut split_tokens = Vec::new();
+
for tok in tokens {
+
for s in split_keep_delimiters(tok.contents.clone()) {
+
split_tokens.push(s);
+
}
+
}
let mut quoted: bool = false;
let mut entered: bool = false;
···
let mut in_token_count = 0;
-
for tok in tokens {
+
for tok in split_tokens {
in_token_count += 1; // This could be a problem if it something got split above..
-
let contents = &tok.contents;
-
if contents.starts_with([' ', '\t']) && !quoted {
+
if tok.starts_with([' ', '\t']) && !quoted {
continue;
}
-
if !entered && contents.starts_with('(') {
+
if !entered && tok.starts_with('(') {
entered = true;
continue;
}
···
continue;
}
-
if !quoted && contents.starts_with(')') {
+
if !quoted && tok.starts_with(')') {
break;
}
let mut i = 0;
-
while i < contents.len() {
-
let c = contents.chars().nth(i).unwrap();
+
while i < tok.len() {
+
let c = tok.chars().nth(i).unwrap();
i += 1;
if c == '\"' {
···
// maybe have the Token struct contain scope level later?
for tok in tokens {
tokens_consumed += 1;
-
-
println!(
-
"Collecting: \"{}\" is brak: {}",
-
tok.contents,
-
tok.contents == "{"
-
);
-
// println!(
-
// "ebrack: {}, scope: {}, entered: {}, escaped: {}",
-
// entering_bracket_count, scope_count, entered, escaped
-
//);
if !entered {
if tok.contents.is_only_whitespace() {
continue;
···
if tok.contents != "{"
// Expected block start, got garbage
{
-
println!("Expected \'{{{{{{\' got \'{}\'", tok.contents);
return (Vec::new(), 0);
}
}
···
}
// Scope Start
-
if tok.contents == "{" {
entering_bracket_count += 1;
+
if entering_bracket_count == 3 {
+
scope_count += 1;
+
entering_bracket_count = 0;
+
if !entered {
+
entered = true;
+
}
+
}
} else {
entering_bracket_count = 0;
}
···
exiting_bracket_count += 1;
if exiting_bracket_count == 3 {
scope_count -= 1;
-
exiting_bracket_count = 0;
-
if scope_count == 0 {
-
//pop the last 2 brackets off and return
-
block.pop();
-
block.pop();
-
break;
-
}
+
entering_bracket_count = 0;
}
} else {
-
exiting_bracket_count = 0;
+
entering_bracket_count = 0;
}
if tok.contents == "\\" {
escaped = true;
} else {
-
if entered {
-
block.push(tok.clone());
-
}
-
}
-
if entering_bracket_count == 3 {
-
scope_count += 1;
-
entering_bracket_count = 0;
-
-
entered = true;
+
block.push(tok.clone());
}
}
return (block, tokens_consumed);
···
return output;
}
-
pub fn strings_to_tokens(in_strings: Vec<String>, origin_file: String) -> Vec<Token> {
+
pub fn strings_to_tokens(in_strings: Vec<String>, origin_file: usize) -> Vec<Token> {
let mut tokens = Vec::new();
let mut line_count: u32 = 1;
···
line_count += 1;
}
}
-
let token: Token = Token::new(str, origin_file.clone(), current_line);
+
let token: Token = Token::new(str, origin_file, current_line);
tokens.push(token);
}
···
// Need to do some special case stuff so you can macros without spaces between
// (something like "stuff!insert(..)" is split to ["stuff","!insert(..)"] so it can be acted on later)
-
pub fn split_to_tokens(instr: String, origin_file: String) -> Vec<Token> {
+
pub fn split_to_tokens(instr: String, origin_file: usize) -> Vec<Token> {
let split = split_keep_delimiters(instr);
let mut new_split: Vec<String> = Vec::new();
for s in split {
···
return strings_to_tokens(new_split, origin_file);
}
+
pub fn next_nonwhitespace_token(tokens: &Vec<Token>, index: usize) -> (bool, usize) {
+
while index < tokens.len() {
+
if tokens[index].contents.is_only_whitespace() {
+
continue;
+
}
+
return (true, index);
+
}
+
return (false, 0);
+
}
+
+
//trim whitespace from the ends
pub fn trim_whitespace_tokens(tokens: &[Token]) -> &[Token] {
let mut start: usize = 0;
let mut end: usize = tokens.len();
···
if !tok.contents.is_only_whitespace() {
break;
}
-
start += 1;
+
start = start + 1;
}
for tok in tokens.iter().rev() {
+
end = end - 1;
if !tok.contents.is_only_whitespace() {
break;
}
-
end -= 1;
}
-
if start < end {
-
return &tokens[start..end];
-
} else {
-
return &[];
-
}
+
return &tokens[start..end];
}
pub trait OnlyWhitespace {
+12 -25
src/types.rs
···
-
use std::sync::Mutex;
+
use std::path::PathBuf;
+
+
use crate::projectparse::ProjectContext;
pub struct Token {
pub contents: String,
-
pub origin_file: String, //TODO: make this an index so we dont have the same string in memory for every single word
+
pub origin_file: usize,
pub line_number: u32,
-
}
-
-
pub enum BlockEdgeType {
-
FileStart,
-
FileEnd,
-
Start,
-
End,
-
}
-
-
// A 'Block' is what im calling the enclosed scope of a macro
-
pub struct BlockEdge {
-
pub edge_type: BlockEdgeType,
-
pub tokens_to_next_edge: u64,
}
pub struct InputFile {
-
pub filename_input: String,
-
pub filename_skidout: String,
-
pub filename_htmlout: String,
+
pub file_input: PathBuf,
+
pub file_skidout: PathBuf,
+
pub file_htmlout: PathBuf,
pub tokens: Vec<Token>,
pub working_index: usize,
-
pub block_edges: Vec<BlockEdge>,
}
-
type MacroExpansion = fn(&mut InputFile, &Vec<String>, &[Token]) -> Vec<Token>;
+
type MacroExpansion = fn(&mut InputFile, &mut ProjectContext, &Vec<String>, &[Token]) -> Vec<Token>;
pub struct Macro<'a> {
pub symbol: &'a str,
pub expand: MacroExpansion,
···
impl InputFile {
pub fn new() -> InputFile {
InputFile {
-
filename_input: "".to_string(),
-
filename_skidout: "".to_string(),
-
filename_htmlout: "".to_string(),
+
file_input: "".into(),
+
file_skidout: "".into(),
+
file_htmlout: "".into(),
tokens: Vec::new(),
working_index: 0,
-
block_edges: Vec::new(),
}
}
}
impl Token {
-
pub fn new(contents: String, origin_file: String, line_number: u32) -> Token {
+
pub fn new(contents: String, origin_file: usize, line_number: u32) -> Token {
Token {
contents: contents,
origin_file: origin_file,