use crate::ported::bindings::config as binding_config;
use crate::ported::commands::config::{get_argparser, StrFunction};
fn tmux_setup(args: &[String]) -> Result<(), String> {
use crate::ported::bindings::config::{init_tmux_environment, sorted_tmux_configs};
use crate::ported::bindings::tmux::{get_tmux_version, set_tmux_environment, source_tmux_file};
use crate::ported::colorscheme::Colorscheme;
use crate::ported::config::TMUX_CONFIG_DIRECTORY;
use crate::ported::lib::config::load_json_config;
use crate::ported::lib::dict::mergedicts;
use crate::ported::lib::encoding::get_preferred_output_encoding;
use crate::ported::renderers::tmux::TmuxRenderer;
use crate::ported::theme::Theme;
use crate::ported::{_find_config_files, get_config_paths, get_default_theme};
use std::path::PathBuf;
let mut explicit: Vec<PathBuf> = Vec::new();
if let Ok(pcp) = std::env::var("POWERLINE_CONFIG_PATHS") {
for p in pcp.split(':').filter(|s| !s.is_empty()) {
explicit.push(PathBuf::from(p));
}
}
let mut i = 0;
while i < args.len() {
if args[i] == "--config-path" || args[i] == "-p" {
if let Some(p) = args.get(i + 1) {
explicit.push(PathBuf::from(p));
}
i += 2;
} else {
i += 1;
}
}
let search_paths: Vec<PathBuf> = if !explicit.is_empty() {
explicit
} else {
let mut paths: Vec<PathBuf> = Vec::new();
if let Some(manifest) = option_env!("CARGO_MANIFEST_DIR") {
let ported = PathBuf::from(manifest).join("src/ported/config_files");
if ported.is_dir() {
paths.push(ported);
}
}
if let Some(cache) = crate::extensions::bundled_config::bundled_config_dir() {
if !paths.contains(&cache) {
paths.push(cache);
}
}
paths.extend(get_config_paths());
paths
};
let load_one = |name: &str| -> Option<serde_json::Map<String, serde_json::Value>> {
let matches = _find_config_files(&search_paths, name).ok()?;
let p = matches.first()?;
let v = load_json_config(p).ok()?;
v.as_object().cloned()
};
let load_cascade = |levels: &[String]| -> Option<serde_json::Map<String, serde_json::Value>> {
let mut out: serde_json::Map<String, serde_json::Value> = serde_json::Map::new();
let mut loaded = 0u32;
for level in levels {
if let Ok(matches) = _find_config_files(&search_paths, level) {
for p in &matches {
if let Ok(v) = load_json_config(p) {
if let Some(o) = v.as_object().cloned() {
mergedicts(&mut out, o, true);
loaded += 1;
}
}
}
}
}
if loaded == 0 {
None
} else {
Some(out)
}
};
let main = load_one("config").ok_or_else(|| "config.json not found".to_string())?;
let colors_json = load_one("colors").ok_or_else(|| "colors.json not found".to_string())?;
let cs_name = main
.get("ext")
.and_then(|e| e.get("tmux"))
.and_then(|t| t.get("colorscheme"))
.and_then(|v| v.as_str())
.unwrap_or("default")
.to_string();
let theme_name = main
.get("ext")
.and_then(|e| e.get("tmux"))
.and_then(|t| t.get("theme"))
.and_then(|v| v.as_str())
.unwrap_or("default")
.to_string();
let cs_levels = vec![
"colorschemes/__main__".to_string(),
format!("colorschemes/tmux/__main__"),
format!("colorschemes/tmux/{}", cs_name),
];
let colorscheme_json =
load_cascade(&cs_levels).ok_or_else(|| "no colorscheme for tmux".to_string())?;
let user_top_theme = main
.get("common")
.and_then(|c| c.get("default_top_theme"))
.and_then(|v| v.as_str())
.map(String::from);
let computed_top_theme = {
let enc = get_preferred_output_encoding().to_lowercase();
get_default_theme(enc.starts_with("utf") || enc.starts_with("ucs"))
};
let top_theme: String = user_top_theme.unwrap_or_else(|| computed_top_theme.to_string());
let theme_levels = vec![
format!("themes/{}", top_theme),
"themes/tmux/__main__".to_string(),
format!("themes/tmux/{}", theme_name),
];
let theme_json = load_cascade(&theme_levels).ok_or_else(|| "no theme for tmux".to_string())?;
let colorscheme = Colorscheme::new(&colorscheme_json, &colors_json);
let mut theme = Theme::new();
if let Some(d) = theme_json.get("dividers").and_then(|v| v.as_object()) {
theme.dividers = d.clone();
}
let term_truecolor = main
.get("common")
.and_then(|c| c.get("term_truecolor"))
.and_then(|v| v.as_bool())
.unwrap_or(false);
let renderer = TmuxRenderer::new(term_truecolor);
let env_vars = init_tmux_environment(&colorscheme, &theme, &renderer, term_truecolor);
for (varname, value) in &env_vars {
set_tmux_environment(varname, value, true);
}
let base_conf = TMUX_CONFIG_DIRECTORY().join("powerline-base.conf");
if base_conf.exists() {
source_tmux_file(base_conf.to_str().unwrap_or(""));
}
if let Some(version) = get_tmux_version(&()) {
for (fname, _priority) in sorted_tmux_configs(&version) {
source_tmux_file(fname.to_str().unwrap_or(""));
}
}
if std::env::var("POWERLINE_COMMAND")
.map(|v| v.is_empty())
.unwrap_or(true)
{
if let Some(cmd) = binding_config::deduce_command() {
set_tmux_environment("POWERLINE_COMMAND", &cmd, false);
}
}
let _ = std::process::Command::new("tmux")
.arg("refresh-client")
.status();
Ok(())
}
pub fn main(args: &[String]) -> i32 {
let _parser = get_argparser();
if args.first().map(String::as_str) == Some("vim") {
match args.get(1).map(String::as_str) {
Some("source-path") => {
match crate::extensions::bundled_config::bundled_vim_plugin_path() {
Some(p) => {
println!("{}", p.display());
return 0;
}
None => {
eprintln!("powerline-config vim source-path: extraction failed");
return 1;
}
}
}
Some(s) => {
eprintln!("powerline-config vim: unknown action '{}'", s);
return 2;
}
None => {
eprintln!("powerline-config vim: missing action argument (expected `source-path`)");
return 2;
}
}
}
let action_name = match args.first().map(String::as_str) {
Some("tmux") | Some("shell") => match args.get(1) {
Some(s) => s.as_str(),
None => {
eprintln!("powerline-config: missing action argument");
return 2;
}
},
Some(s) => s,
None => {
eprintln!("powerline-config: missing function argument");
return 2;
}
};
let _binding = binding_config::deduce_command();
let resolved: Option<StrFunction> =
crate::ported::commands::config::tmux_action_from_name(action_name)
.or_else(|| crate::ported::commands::config::shell_action_from_name(action_name));
match resolved {
Some(StrFunction::Command) => {
match binding_config::deduce_command() {
Some(cmd) => {
println!("{}", cmd);
0
}
None => 1,
}
}
Some(StrFunction::Uses) => {
let component = match args.get(1) {
Some(c) => c.clone(),
None => {
eprintln!("powerline-config uses: component required");
return 1;
}
};
let mut shell: Option<String> = None;
let mut i = 2;
while i < args.len() {
if args[i] == "-s" || args[i] == "--shell" {
if let Some(s) = args.get(i + 1) {
shell = Some(s.clone());
}
i += 2;
} else {
i += 1;
}
}
let sh_list: Vec<String> = match shell.as_deref() {
Some(s) => vec![s.to_string(), "shell".to_string()],
None => vec!["shell".to_string()],
};
for sh in &sh_list {
let varname = format!(
"POWERLINE_NO_{}_{}",
sh.to_uppercase(),
component.to_uppercase()
);
if std::env::var(&varname)
.map(|v| !v.is_empty())
.unwrap_or(false)
{
return 1;
}
}
use crate::ported::lib::config::load_json_config;
use crate::ported::lib::dict::mergedicts;
use crate::ported::{_find_config_files, get_config_paths};
let mut search_paths: Vec<std::path::PathBuf> = Vec::new();
if let Ok(pcp) = std::env::var("POWERLINE_CONFIG_PATHS") {
for p in pcp.split(':').filter(|s| !s.is_empty()) {
search_paths.push(std::path::PathBuf::from(p));
}
}
let mut j = 0;
while j < args.len() {
if args[j] == "--config-path" || args[j] == "-p" {
if let Some(p) = args.get(j + 1) {
search_paths.push(std::path::PathBuf::from(p));
}
j += 2;
} else {
j += 1;
}
}
search_paths.extend(get_config_paths());
let mut config = serde_json::Map::new();
if let Ok(matches) = _find_config_files(&search_paths, "config") {
for path in matches {
if let Ok(v) = load_json_config(&path) {
if let Some(o) = v.as_object().cloned() {
mergedicts(&mut config, o, true);
}
}
}
}
let components: Vec<String> = config
.get("ext")
.and_then(|v| v.as_object())
.and_then(|o| o.get("shell"))
.and_then(|v| v.as_object())
.and_then(|o| o.get("components"))
.and_then(|v| v.as_array())
.map(|a| {
a.iter()
.filter_map(|v| v.as_str().map(String::from))
.collect()
})
.unwrap_or_else(|| vec!["tmux".to_string(), "prompt".to_string()]);
if components.iter().any(|c| c == &component) {
0
} else {
1
}
}
Some(StrFunction::Setup) => {
match tmux_setup(args) {
Ok(()) => 0,
Err(e) => {
eprintln!("powerline-config: tmux setup failed: {}", e);
1
}
}
}
Some(StrFunction::Source) | Some(StrFunction::Setenv) => {
eprintln!(
"powerline-config: action '{}' requires the Powerline orchestrator (deferred port)",
action_name
);
1
}
None => {
eprintln!("powerline-config: unknown function '{}'", action_name);
2
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn main_returns_2_when_no_args() {
assert_eq!(main(&[]), 2);
}
#[test]
fn main_returns_2_for_unknown_function() {
let r = main(&["definitely-not-a-real-function".to_string()]);
assert_eq!(r, 2);
}
#[test]
fn main_returns_1_for_tmux_function_requiring_powerline_orchestrator() {
let r = main(&["source".to_string()]);
assert_eq!(r, 1);
}
#[test]
fn main_handles_known_shell_function() {
let r = main(&["command".to_string()]);
assert!(r == 0 || r == 1, "unexpected exit code {}", r);
}
}