use std::path::PathBuf;
use std::sync::OnceLock;
static CELL: OnceLock<Option<PathBuf>> = OnceLock::new();
const BUNDLED: &[(&str, &str)] = &[
(
"colors.json",
include_str!("../ported/config_files/colors.json"),
),
(
"config.json",
include_str!("../ported/config_files/config.json"),
),
(
"colorschemes/default.json",
include_str!("../ported/config_files/colorschemes/default.json"),
),
(
"colorschemes/solarized.json",
include_str!("../ported/config_files/colorschemes/solarized.json"),
),
(
"colorschemes/ipython/__main__.json",
include_str!("../ported/config_files/colorschemes/ipython/__main__.json"),
),
(
"colorschemes/pdb/__main__.json",
include_str!("../ported/config_files/colorschemes/pdb/__main__.json"),
),
(
"colorschemes/pdb/default.json",
include_str!("../ported/config_files/colorschemes/pdb/default.json"),
),
(
"colorschemes/pdb/solarized.json",
include_str!("../ported/config_files/colorschemes/pdb/solarized.json"),
),
(
"colorschemes/shell/__main__.json",
include_str!("../ported/config_files/colorschemes/shell/__main__.json"),
),
(
"colorschemes/shell/default.json",
include_str!("../ported/config_files/colorschemes/shell/default.json"),
),
(
"colorschemes/shell/solarized.json",
include_str!("../ported/config_files/colorschemes/shell/solarized.json"),
),
(
"colorschemes/tmux/default.json",
include_str!("../ported/config_files/colorschemes/tmux/default.json"),
),
(
"colorschemes/tmux/solarized.json",
include_str!("../ported/config_files/colorschemes/tmux/solarized.json"),
),
(
"colorschemes/vim/__main__.json",
include_str!("../ported/config_files/colorschemes/vim/__main__.json"),
),
(
"colorschemes/vim/default.json",
include_str!("../ported/config_files/colorschemes/vim/default.json"),
),
(
"colorschemes/vim/solarized.json",
include_str!("../ported/config_files/colorschemes/vim/solarized.json"),
),
(
"colorschemes/vim/solarizedlight.json",
include_str!("../ported/config_files/colorschemes/vim/solarizedlight.json"),
),
(
"themes/ascii.json",
include_str!("../ported/config_files/themes/ascii.json"),
),
(
"themes/powerline.json",
include_str!("../ported/config_files/themes/powerline.json"),
),
(
"themes/powerline_terminus.json",
include_str!("../ported/config_files/themes/powerline_terminus.json"),
),
(
"themes/powerline_unicode7.json",
include_str!("../ported/config_files/themes/powerline_unicode7.json"),
),
(
"themes/unicode.json",
include_str!("../ported/config_files/themes/unicode.json"),
),
(
"themes/unicode_terminus.json",
include_str!("../ported/config_files/themes/unicode_terminus.json"),
),
(
"themes/unicode_terminus_condensed.json",
include_str!("../ported/config_files/themes/unicode_terminus_condensed.json"),
),
(
"themes/ipython/in.json",
include_str!("../ported/config_files/themes/ipython/in.json"),
),
(
"themes/ipython/in2.json",
include_str!("../ported/config_files/themes/ipython/in2.json"),
),
(
"themes/ipython/out.json",
include_str!("../ported/config_files/themes/ipython/out.json"),
),
(
"themes/ipython/rewrite.json",
include_str!("../ported/config_files/themes/ipython/rewrite.json"),
),
(
"themes/pdb/default.json",
include_str!("../ported/config_files/themes/pdb/default.json"),
),
(
"themes/shell/__main__.json",
include_str!("../ported/config_files/themes/shell/__main__.json"),
),
(
"themes/shell/continuation.json",
include_str!("../ported/config_files/themes/shell/continuation.json"),
),
(
"themes/shell/default.json",
include_str!("../ported/config_files/themes/shell/default.json"),
),
(
"themes/shell/default_leftonly.json",
include_str!("../ported/config_files/themes/shell/default_leftonly.json"),
),
(
"themes/shell/select.json",
include_str!("../ported/config_files/themes/shell/select.json"),
),
(
"themes/tmux/default.json",
include_str!("../ported/config_files/themes/tmux/default.json"),
),
(
"themes/vim/__main__.json",
include_str!("../ported/config_files/themes/vim/__main__.json"),
),
(
"themes/vim/cmdwin.json",
include_str!("../ported/config_files/themes/vim/cmdwin.json"),
),
(
"themes/vim/default.json",
include_str!("../ported/config_files/themes/vim/default.json"),
),
(
"themes/vim/help.json",
include_str!("../ported/config_files/themes/vim/help.json"),
),
(
"themes/vim/plugin_commandt.json",
include_str!("../ported/config_files/themes/vim/plugin_commandt.json"),
),
(
"themes/vim/plugin_gundo-preview.json",
include_str!("../ported/config_files/themes/vim/plugin_gundo-preview.json"),
),
(
"themes/vim/plugin_gundo.json",
include_str!("../ported/config_files/themes/vim/plugin_gundo.json"),
),
(
"themes/vim/plugin_nerdtree.json",
include_str!("../ported/config_files/themes/vim/plugin_nerdtree.json"),
),
(
"themes/vim/quickfix.json",
include_str!("../ported/config_files/themes/vim/quickfix.json"),
),
(
"themes/vim/tabline.json",
include_str!("../ported/config_files/themes/vim/tabline.json"),
),
(
"themes/wm/default.json",
include_str!("../ported/config_files/themes/wm/default.json"),
),
];
pub fn bundled_config_dir() -> Option<PathBuf> {
CELL.get_or_init(extract).clone()
}
fn extract() -> Option<PathBuf> {
let cache = std::env::var_os("XDG_CACHE_HOME")
.map(PathBuf::from)
.or_else(|| std::env::var_os("HOME").map(|h| PathBuf::from(h).join(".cache")))
.unwrap_or_else(std::env::temp_dir)
.join("powerliners")
.join("config_files");
if std::fs::create_dir_all(&cache).is_err() {
return None;
}
for (rel, content) in BUNDLED {
let target = cache.join(rel);
if let Some(parent) = target.parent() {
let _ = std::fs::create_dir_all(parent);
}
let _ = std::fs::write(&target, content);
}
cache.join("config.json").exists().then_some(cache)
}
pub fn bundled_vim_plugin_path() -> Option<PathBuf> {
const VIM_PLUGIN: &str = include_str!("../ported/bindings/vim/powerline.vim");
let dir = std::env::var_os("XDG_CACHE_HOME")
.map(PathBuf::from)
.or_else(|| std::env::var_os("HOME").map(|h| PathBuf::from(h).join(".cache")))
.unwrap_or_else(std::env::temp_dir)
.join("powerliners")
.join("vim");
std::fs::create_dir_all(&dir).ok()?;
let path = dir.join("powerline.vim");
std::fs::write(&path, VIM_PLUGIN).ok()?;
Some(path)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn extraction_lands_load_bearing_shell_files() {
let dir = bundled_config_dir().expect("cache dir extractable");
assert!(dir.join("colorschemes/shell/default.json").exists());
assert!(dir.join("colorschemes/shell/__main__.json").exists());
assert!(dir.join("themes/shell/default.json").exists());
assert!(dir.join("config.json").exists());
assert!(dir.join("colors.json").exists());
}
#[test]
fn all_46_files_extracted() {
let dir = bundled_config_dir().expect("cache dir extractable");
for (rel, _) in BUNDLED {
assert!(dir.join(rel).exists(), "expected extracted file at {rel}");
}
assert_eq!(BUNDLED.len(), 46);
}
#[test]
fn every_bundled_json_entry_is_valid_json() {
let mut json_count = 0usize;
for (rel, content) in BUNDLED {
if !rel.ends_with(".json") {
continue;
}
json_count += 1;
let parsed: Result<serde_json::Value, _> = serde_json::from_str(content);
assert!(
parsed.is_ok(),
"bundled entry {rel} failed JSON parse: {err}",
err = parsed.err().unwrap()
);
}
assert_eq!(
json_count,
BUNDLED.len(),
"non-JSON asset in BUNDLED — extend the sweep above"
);
}
}