Skip to content

Commit

Permalink
feat: Refactor + Wrap up port
Browse files Browse the repository at this point in the history
  • Loading branch information
sgoudham committed Aug 27, 2022
1 parent abc8986 commit 9ba9813
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 80 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ log = "0.4.17"
semver = "1.0.12"
serde_json = "1.0.82"
toml_edit = "0.14.4"
version-compare = "0.1.0"
126 changes: 81 additions & 45 deletions src/bin/mdbook-catppuccin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,6 @@ pub fn make_app() -> Command<'static> {
)
.subcommand(
Command::new("install")
.arg(
Arg::new("assets-dir")
.default_value(".")
.long("assets-dir")
.help("Relative directory for the assets, from the book directory root")
)
.arg(
Arg::new("dir")
.default_value(".")
Expand Down Expand Up @@ -81,39 +75,45 @@ fn handle_supports(pre: &Catppuccin, sub_args: &ArgMatches) -> ! {
}
}

// all selection highlights -> surface0
// function docs (shift+k) -> mantle

mod install {
use std::fs::File;
use std::io::Write;
use std::io::{ErrorKind, Write};
use std::path::Path;
use std::{fs, path::PathBuf};

use clap::ArgMatches;
use log::{error, info, warn};
use toml_edit::{Document, Value};

use mdbook_catppuccin::toml::{ArrayExt, DocumentExt};
use mdbook_catppuccin::{
toml::{ArrayExt, DocumentExt, TableExt},
TomlPath,
};

const VERSION: &str = env!("CARGO_PKG_VERSION");
const CATPPUCCIN_ASSETS: &[(&str, &str, &[u8])] = &[
(
"catppuccin.js",
"additional-js",
include_bytes!("./assets/catppuccin.js"),
),
const CATPPUCCIN_ASSETS: &[(&str, TomlPath, &[u8])] = &[
(
"catppuccin.css",
"additional-css",
TomlPath::Path("additional-css"),
include_bytes!("./assets/catppuccin.css"),
),
(
"catppuccin-highlight.css",
"additional-css",
TomlPath::Path("additional-css"),
include_bytes!("./assets/catppuccin-highlight.css"),
),
(
"index.hbs",
TomlPath::None,
include_bytes!("./assets/index.hbs"),
),
];

pub(crate) fn handle_install(sub_args: &ArgMatches) {
let dir = sub_args.get_one::<String>("dir").unwrap();
let assets_dir = sub_args.get_one::<String>("assets-dir").unwrap();
let project_dir = PathBuf::from(dir);
let toml_config = project_dir.join("book.toml");

Expand All @@ -125,13 +125,44 @@ mod install {
return;
}

let (toml, mut document) = read_configuration_file(&toml_config);
let theme_dir = populate_theme_directory(&document, &project_dir);
copy_assets(&mut document, &theme_dir);
update_configuration_file(document, toml, toml_config);

info!("mdbook-catppuccin is now installed. Build your book with `mdbook build` to try out your new catppuccin colour palettes!");
}

fn read_configuration_file(toml_config: &PathBuf) -> (String, Document) {
info!("Reading configuration file '{}'", toml_config.display());
let toml = fs::read_to_string(&toml_config).expect("Can't read configuration file");
let mut document = toml
.parse::<Document>()
.expect("Configuration is not valid TOML");
let document = toml.parse::<Document>()
.expect("Configuration is not valid TOML");
(toml, document)
}

fn populate_theme_directory(document: &Document, project_dir: &Path) -> PathBuf {
let mut theme_dir = project_dir.join("theme");

if let Ok(output_html) = document.get_output_html() {
if let Ok(theme) = output_html.theme() {
theme_dir = project_dir.join(theme)
}
} else {
warn!("Unexpected configuration, defaulting to default 'theme' directory for transfering assets");
}

if let Err(err) = fs::create_dir_all(&theme_dir) {
if err.kind() == ErrorKind::PermissionDenied {
warn!("Permission to create '{}' denied", theme_dir.display())
}
}

if let Ok(preprocessor) = document.get_or_insert_into_preprocessor_mut("catppuccin") {
theme_dir
}

fn copy_assets(document: &mut Document, theme_dir: &Path) {
if let Ok(preprocessor) = document.insert_into_preprocessor("catppuccin") {
let value = toml_edit::value(Value::from(VERSION.trim()).decorated(
" ",
" # DO NOT EDIT: Managed by `mdbook-catppuccin install`",
Expand All @@ -142,31 +173,44 @@ mod install {
};

for (name, entry, content) in CATPPUCCIN_ASSETS {
let path = if assets_dir == "." {
project_dir.join(name)
} else {
project_dir.join(assets_dir).join(name)
};
let path = theme_dir.join(name);
let path_str = path.to_str().expect("Non-UTF8 Filepath");

if let Ok(asset) = document.get_or_insert_into_output_html_mut(entry) {
if !asset.contains_str(path_str) {
info!("Adding '{path_str}' to '{entry}'");
asset.push(path_str);
if let TomlPath::Path(path) = entry {
if let Ok(asset) = document.insert_into_output_html(path) {
if !asset.contains_str(path_str) {
info!("Adding '{path_str}' to '{path}'");
asset.push(path_str);
}
} else {
warn!("Unexpected configuration, not updating '{path}'");
}
} else {
warn!("Unexpected configuration, not updating '{entry}'");
}

info!(
"Copying '{name}' to '{filepath}'",
filepath = path.display()
);
let mut file = File::create(path).expect("Can't open file for writing");
if *name == "index.hbs" {
let file_exists = Path::new(path_str).try_exists();
if let Ok(val) = file_exists {
if val {
info!(
"'{}' already exists and therefore will not be overwritten",
path.display()
);
break;
}
} else {
error!("Unexpected error, cannot determine if 'index.hbs' exists");
break;
}
}

info!("Copying '{name}' to '{path_str}'");
let mut file = File::create(path_str).expect("Can't open file for writing");
file.write_all(content)
.expect("Can't write content to file");
}
}

fn update_configuration_file(document: Document, toml: String, toml_config: PathBuf) {
let new_toml = document.to_string();
if new_toml != toml {
info!(
Expand All @@ -183,13 +227,5 @@ mod install {
toml_config.display()
);
}

info!("mdbook-catppuccin is now installed. Build your book with `mdbook build` to see your new catppuccin colour palettes in action!");
}

fn read_configuration_file() {}

fn copy_assets() {}

fn update_configuration_file() {}
}
44 changes: 34 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pub mod toml;

use std::process;
use version_compare::{compare, Cmp};

use log::error;
use mdbook::book::Book;
Expand All @@ -9,28 +9,52 @@ use mdbook::preprocess::{Preprocessor, PreprocessorContext};

pub struct Catppuccin;

const VERSION: &str = env!("CARGO_PKG_VERSION");
pub enum TomlPath {
Path(&'static str),
None,
}

const LATEST_ASSETS_VERSION: &str = env!("CARGO_PKG_VERSION");
const TOML_KEY: &str = "preprocessor.catppuccin.assets_version";

impl Catppuccin {
pub fn new() -> Self {
Catppuccin
}
}

impl Default for Catppuccin {
fn default() -> Self {
Self::new()
}
}

impl Preprocessor for Catppuccin {
fn name(&self) -> &str {
"catppuccin"
}

fn run(&self, ctx: &PreprocessorContext, book: Book) -> Result<Book> {
let html_config = ctx.config.html_config().unwrap_or_else(|| {
error!("Could not parse 'output.html' field");
process::exit(1);
});
let theme_dir = match html_config.theme {
Some(ref theme) => ctx.root.join(theme),
None => ctx.root.join("theme"),
};
match ctx.config.get(TOML_KEY).and_then(|key| key.as_str()) {
Some(current_assets_version) => {
if let Ok(cmp) = compare(LATEST_ASSETS_VERSION, current_assets_version) {
match cmp {
Cmp::Lt => error!(
r#"mdbook-catppuccin with version '{LATEST_ASSETS_VERSION}' is out of date with current asset version '{current_assets_version}'.
Please upgrade by running 'cargo install --force mdbook-catppuccin' and then re-run 'mdbook-catppuccin install'"#
),
Cmp::Gt => error!(
r#"Out-Of-Date Asset Version '{current_assets_version}' Found: Please update your version of 'mdbook-catppuccin' to '{LATEST_ASSETS_VERSION}'.
Then run 'mdbook-catppuccin install' to install the lastest assets."#
),
_ => {}
}
}
}
None => {
error!("Unable to check assets_version, key '{TOML_KEY}' is not a string");
}
}

Ok(book)
}
Expand Down
69 changes: 44 additions & 25 deletions src/toml.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,25 @@
use toml_edit::{Array, Document, Item, Table, Value};

pub trait DocumentExt {
fn get_or_insert_into_output_html_mut(&mut self, entry: &str) -> Result<&mut Array, String>;
fn get_or_insert_into_preprocessor_mut(&mut self, entry: &str) -> Result<&mut Item, &str>;
fn get_output_html(&self) -> Result<&Table, String>;
fn insert_into_output_html(&mut self, entry: &str) -> Result<&mut Array, String>;
fn insert_into_preprocessor(&mut self, entry: &str) -> Result<&mut Item, &str>;
fn insert_dotted_table(&mut self, name: &str) -> Result<&mut Table, String>;
}

impl DocumentExt for Document {
fn insert_dotted_table(&mut self, name: &str) -> Result<&mut Table, String> {
let table_vec = name.split('.').collect::<Vec<&str>>();
if table_vec.len() == 1 || name == "." {
return Err(format!("No dotted table found in '{}'", name));
}

let mut table = self
.entry(table_vec.first().unwrap())
.or_insert(Item::Table(Table::new()))
.as_table_mut()
.unwrap();
for key in table_vec.iter().skip(1) {
table = table
.entry(key)
.or_insert(Item::Table(Table::new()))
.as_table_mut()
.unwrap()
}

Ok(table)
fn get_output_html(&self) -> Result<&Table, String> {
self.get("output")
.ok_or_else(|| "No key 'output'".to_owned())?
.as_table()
.ok_or_else(|| "'output' is not a table".to_owned())?
.get("html")
.ok_or_else(|| "No key 'output.html'".to_owned())?
.as_table()
.ok_or_else(|| "'output.html' is not a table".into())
}

fn get_or_insert_into_output_html_mut(&mut self, entry: &str) -> Result<&mut Array, String> {
fn insert_into_output_html(&mut self, entry: &str) -> Result<&mut Array, String> {
let empty_table = Item::Table(Table::default());
let empty_array = Item::Value(Value::Array(Array::default()));

Expand All @@ -48,7 +38,7 @@ impl DocumentExt for Document {
.ok_or(format!("Could not insert 'output.html.{}'", entry))
}

fn get_or_insert_into_preprocessor_mut(&mut self, entry: &str) -> Result<&mut Item, &str> {
fn insert_into_preprocessor(&mut self, entry: &str) -> Result<&mut Item, &str> {
let empty_table = Item::Table(Table::default());
let table = self
.entry("preprocessor")
Expand All @@ -59,12 +49,35 @@ impl DocumentExt for Document {
.or_insert(empty_table);
Ok(table)
}

fn insert_dotted_table(&mut self, name: &str) -> Result<&mut Table, String> {
let table_vec = name.split('.').collect::<Vec<&str>>();
if table_vec.len() == 1 || name == "." {
return Err(format!("No dotted table found in '{}'", name));
}

let mut table = self
.entry(table_vec.first().unwrap())
.or_insert(Item::Table(Table::new()))
.as_table_mut()
.unwrap();
for key in table_vec.iter().skip(1) {
table = table
.entry(key)
.or_insert(Item::Table(Table::new()))
.as_table_mut()
.unwrap()
}

Ok(table)
}
}

pub(crate) trait TableExt {
pub trait TableExt {
fn contains(&self, key: &str) -> bool;
fn additional_css(&self) -> Result<&Array, &str>;
fn additional_js(&self) -> Result<&Array, &str>;
fn theme(&self) -> Result<&'_ str, &str>;
}

impl TableExt for Table {
Expand All @@ -83,6 +96,12 @@ impl TableExt for Table {
.and_then(|item| item.as_array())
.ok_or("'additional-js' not found")
}

fn theme(&self) -> Result<&'_ str, &str> {
self.get("theme")
.and_then(|item| item.as_str())
.ok_or("'theme' not found")
}
}

pub trait ArrayExt {
Expand Down

0 comments on commit 9ba9813

Please sign in to comment.