use std::{
path::{Path, PathBuf},
vec,
};
use comrak::Options;
use serde::{Deserialize, Serialize};
use tracing::warn;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct AppConfig {
pub root_title: String,
pub root_description: String,
pub root_readme: String,
pub snapshots: Vec<String>,
pub favicon: String,
pub logo: String,
pub logo_alt: String,
pub logo_link: String,
pub enable_http_clone: bool,
pub enable_index_links: bool,
pub enable_index_owner: bool,
pub clone_prefix: String,
pub ssh_clone_prefix: String,
pub max_commit_message_length: usize,
pub max_repo_desc_length: usize,
pub css: Option<String>,
pub robots_txt: Option<String>,
pub header: Option<HeaderStyle>,
}
impl ::std::default::Default for AppConfig {
fn default() -> Self {
Self {
root_title: String::from("Git repository browser"),
root_description: String::new(),
root_readme: String::new(),
snapshots: vec![
String::from("tar.gz"),
String::from("tar.bz2"),
String::from("zip"),
],
favicon: String::new(),
logo: String::from("🏡"),
logo_alt: String::new(),
logo_link: String::from("/"),
enable_http_clone: true,
enable_index_links: false,
enable_index_owner: true,
clone_prefix: String::new(),
ssh_clone_prefix: String::new(),
max_commit_message_length: 0,
max_repo_desc_length: 0,
css: None,
robots_txt: None,
header: Some(HeaderStyle::default()),
}
}
}
impl AppConfig {
pub fn load(path: String) -> Self {
confy::load_path(path).unwrap_or_default()
}
pub fn http_clone_enabled(&self) -> bool {
(!self.clone_prefix.is_empty() || !self.ssh_clone_prefix.is_empty())
&& self.enable_http_clone
}
pub fn root_readme_is_valid(&self) -> bool {
Path::new(&self.root_readme).try_exists().unwrap_or(false)
}
pub fn get_css_data(&self) -> Box<[u8]> {
self.css
.as_ref()
.map(|file| {
let format = rsass::output::Format {
style: rsass::output::Style::Compressed,
..rsass::output::Format::default()
};
rsass::compile_scss_path(&PathBuf::from(file), format).unwrap_or_else(|_| {
warn!(
"Unable to load or build css from path {:?}. Using defaults.",
file
);
Vec::new()
})
})
.unwrap_or_else(|| Vec::new())
.into_boxed_slice()
}
}
pub struct ReadmeConfig;
impl ReadmeConfig {
pub fn gfm() -> Options {
let mut options = Options::default();
options.extension.autolink = true;
options.extension.footnotes = true;
options.extension.strikethrough = true;
options.extension.table = true;
options.extension.tagfilter = true;
options.extension.tasklist = true;
options
}
}
#[derive(Debug, Default, Clone, Serialize, PartialEq, Eq)]
pub enum HeaderStyle {
#[default]
Default,
Text,
Image,
}
impl<'de> Deserialize<'de> for HeaderStyle {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let variant: &str = Deserialize::deserialize(deserializer)?;
match variant.to_lowercase().as_str() {
"default" => Ok(HeaderStyle::Default),
"text" => Ok(HeaderStyle::Text),
"image" => Ok(HeaderStyle::Image),
_ => Ok(HeaderStyle::Default),
}
}
}