Initial commit
This commit is contained in:
+25
@@ -0,0 +1,25 @@
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
/// Markdown to HTML converter
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(name = "md-to-html")]
|
||||
#[command(about = "Markdown to HTML converter", long_about = None)]
|
||||
pub struct Cli {
|
||||
#[command(subcommand)]
|
||||
pub command: Commands,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum Commands {
|
||||
/// Convert markdown files to html
|
||||
#[command(arg_required_else_help = true)]
|
||||
Convert {
|
||||
/// The remote to clone
|
||||
#[clap(short, long)]
|
||||
file: String,
|
||||
|
||||
/// Output file
|
||||
#[clap(short, long)]
|
||||
to: Option<String>,
|
||||
},
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct File {
|
||||
title: String,
|
||||
content: String,
|
||||
}
|
||||
|
||||
impl Default for File {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl File {
|
||||
pub fn new() -> File {
|
||||
File {
|
||||
title: String::from("Unknown"),
|
||||
content: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_from_file(&mut self, location: &Path) {
|
||||
let mut file = std::fs::File::open(location).unwrap();
|
||||
file.read_to_string(&mut self.content).unwrap();
|
||||
}
|
||||
|
||||
pub fn get_title(&self) -> &String {
|
||||
&self.title
|
||||
}
|
||||
|
||||
pub fn get_content(&self) -> &String {
|
||||
&self.content
|
||||
}
|
||||
|
||||
pub fn set_title(&mut self, title: String) {
|
||||
self.title = title;
|
||||
}
|
||||
|
||||
pub fn exists(&self) -> bool {
|
||||
std::path::Path::new(&self.title).exists()
|
||||
}
|
||||
|
||||
pub fn write_content(&mut self, content: String) {
|
||||
self.content = content;
|
||||
}
|
||||
|
||||
pub fn erase_content(&mut self) {
|
||||
self.content.clear();
|
||||
}
|
||||
|
||||
pub fn append_content(&mut self, content: &str) {
|
||||
self.content.push_str(content);
|
||||
}
|
||||
|
||||
pub fn read_file(&mut self, location: &str) {
|
||||
let mut file = std::fs::File::open(location).unwrap();
|
||||
file.read_to_string(&mut self.content).unwrap();
|
||||
}
|
||||
|
||||
pub fn write_file(&self) {
|
||||
let mut file = std::fs::File::create(&self.title).unwrap();
|
||||
file.write_all(self.content.as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
pub struct Html {}
|
||||
|
||||
impl Default for Html {
|
||||
fn default() -> Html {
|
||||
Html::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Html {
|
||||
pub fn new() -> Html {
|
||||
Html {}
|
||||
}
|
||||
|
||||
pub fn render(&self, content: &str, minify_html: bool) -> String {
|
||||
let rendered = format!(
|
||||
r#"{}"#,
|
||||
content
|
||||
);
|
||||
|
||||
match minify_html {
|
||||
true => self.minify(rendered, false),
|
||||
false => rendered,
|
||||
}
|
||||
}
|
||||
|
||||
fn minify(&self, content: String, space: bool) -> String {
|
||||
let mut compressed = String::new();
|
||||
|
||||
// Remove all newlines
|
||||
for line in content.lines() {
|
||||
compressed.push_str(line.trim());
|
||||
}
|
||||
|
||||
if space {
|
||||
// Remove all whitespace
|
||||
compressed.retain(|c| !c.is_whitespace());
|
||||
}
|
||||
|
||||
compressed
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod file;
|
||||
pub mod html;
|
||||
pub mod parser;
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
mod args;
|
||||
|
||||
use args::{Cli, Commands};
|
||||
use clap::Parser;
|
||||
use md_to_html::{file, html, parser};
|
||||
use std::path::Path;
|
||||
use std::process::exit;
|
||||
|
||||
fn main() {
|
||||
let args = Cli::parse();
|
||||
let parser = parser::Parser::new(markdown::Options::gfm());
|
||||
let html = html::Html::new();
|
||||
|
||||
match args.command {
|
||||
Commands::Convert { file, to } => {
|
||||
// File preprocessing
|
||||
let location = Path::new(&file);
|
||||
let mut input = file::File::new();
|
||||
let mut output = file::File::new();
|
||||
|
||||
// if file exists, read it
|
||||
if location.exists() {
|
||||
input.read_from_file(location);
|
||||
} else {
|
||||
println!("File does not exist");
|
||||
exit(1)
|
||||
}
|
||||
|
||||
let parsed = parser.parse(input.get_content());
|
||||
let render = html.render(parsed.as_str(), false);
|
||||
|
||||
// Convert all necessary characters
|
||||
let render = render
|
||||
.replace("<", "<")
|
||||
.replace(">", ">")
|
||||
.replace(""", "\"");
|
||||
|
||||
output.write_content(render);
|
||||
|
||||
match to {
|
||||
Some(out) => {
|
||||
output.set_title(out);
|
||||
output.write_file();
|
||||
}
|
||||
None => {
|
||||
output.set_title("out.html".to_string());
|
||||
output.write_file();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
use markdown::Options;
|
||||
|
||||
pub struct Parser {
|
||||
options: Option<Options>,
|
||||
}
|
||||
|
||||
impl Parser {
|
||||
pub fn new(opt: Options) -> Parser {
|
||||
Parser { options: Some(opt) }
|
||||
}
|
||||
|
||||
pub fn parse(&self, content: &str) -> String {
|
||||
match &self.options {
|
||||
Some(opt) => markdown::to_html_with_options(content, opt).unwrap(),
|
||||
None => markdown::to_html(content),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user