use super::super::{PowerlineWidth, Separator, DEFAULT_SEGMENT_IDS};
use crate::config;
pub(super) fn single_line_entries(
line_cfg: Option<&config::LineConfig>,
warn: &mut impl FnMut(&str),
) -> Vec<Vec<config::LineEntry>> {
if let Some(line) = line_cfg {
if line.segments.is_empty() {
warn("[line].segments is empty; no segments will render");
}
}
let entries: Vec<config::LineEntry> = match line_cfg {
Some(l) => l.segments.clone(),
None => DEFAULT_SEGMENT_IDS
.iter()
.map(|&s| config::LineEntry::Id(s.to_string()))
.collect(),
};
vec![entries]
}
pub(super) fn validated_numbered_lines(
line_cfg: Option<&config::LineConfig>,
warn: &mut impl FnMut(&str),
) -> Option<Vec<Vec<config::LineEntry>>> {
let line = line_cfg?;
if line.numbered.is_empty() {
return None;
}
let mut valid: Vec<(u32, Vec<config::LineEntry>)> = line
.numbered
.iter()
.filter_map(|(key, value)| {
if !matches!(value, toml::Value::Table(_)) {
warn(&format!(
"[line] has unknown key '{key}' ({}); expected `[line.N]` sub-tables only. Skipping.",
describe_toml_value(value)
));
return None;
}
let n = match key.parse::<u32>() {
Ok(n) if n > 0 => n,
_ => {
warn(&format!(
"[line.{key}] is not a positive integer key; skipping"
));
return None;
}
};
extract_line_segments(key, value, warn).map(|segs| (n, segs))
})
.collect();
if valid.is_empty() {
return None;
}
valid.sort_by_key(|(n, _)| *n);
for (n, segs) in &valid {
if segs.is_empty() {
warn(&format!(
"[line.{n}].segments is empty; that line will render nothing"
));
}
}
Some(valid.into_iter().map(|(_, segs)| segs).collect())
}
fn extract_line_segments(
key: &str,
value: &toml::Value,
warn: &mut impl FnMut(&str),
) -> Option<Vec<config::LineEntry>> {
let table = match value {
toml::Value::Table(t) => t,
other => {
warn(&format!(
"[line] key '{key}' is a {} (expected a sub-table with `segments = [...]`); skipping",
describe_toml_value(other)
));
return None;
}
};
let segments_value = match table.get("segments") {
Some(v) => v,
None => {
warn(&format!("[line.{key}] has no `segments` array; skipping"));
return None;
}
};
let array = match segments_value {
toml::Value::Array(a) => a,
other => {
warn(&format!(
"[line.{key}].segments is a {} (expected an array of strings or inline tables); skipping",
describe_toml_value(other)
));
return None;
}
};
let mut segs = Vec::with_capacity(array.len());
for (i, item) in array.iter().enumerate() {
match item {
toml::Value::String(s) => segs.push(config::LineEntry::Id(s.clone())),
toml::Value::Table(t) => {
match toml::Value::Table(t.clone()).try_into::<config::LineEntryItem>() {
Ok(parsed) => segs.push(config::LineEntry::Item(parsed)),
Err(e) => warn(&format!(
"[line.{key}].segments[{i}] inline table didn't parse ({e}); skipping that item",
)),
}
}
other => {
warn(&format!(
"[line.{key}].segments[{i}] is a {} (expected a string or inline table); skipping that item",
describe_toml_value(other)
));
}
}
}
Some(segs)
}
fn describe_toml_value(v: &toml::Value) -> &'static str {
match v {
toml::Value::String(_) => "string",
toml::Value::Integer(_) => "integer",
toml::Value::Float(_) => "float",
toml::Value::Boolean(_) => "boolean",
toml::Value::Datetime(_) => "datetime",
toml::Value::Array(_) => "array",
toml::Value::Table(_) => "table",
}
}
pub(super) fn resolve_layout_separator(
config: Option<&config::Config>,
warn: &mut impl FnMut(&str),
) -> Separator {
let powerline_width = config
.and_then(|c| c.layout_options.as_ref())
.and_then(|lo| lo.powerline_width)
.map(|w| validate_powerline_width(w, warn))
.unwrap_or_default();
config
.and_then(|c| c.layout_options.as_ref())
.and_then(|lo| lo.separator.as_deref())
.map(|s| parse_layout_separator(s, powerline_width, warn))
.unwrap_or(Separator::Space)
}
pub(super) fn parse_layout_separator(
value: &str,
powerline_width: PowerlineWidth,
warn: &mut impl FnMut(&str),
) -> Separator {
if value.is_empty() {
return Separator::None;
}
let normalized = value.trim().to_ascii_lowercase();
match normalized.as_str() {
"space" => Separator::Space,
"powerline" => Separator::Powerline {
width: powerline_width,
},
"capsule" | "flex" => {
warn(&format!(
"[layout_options].separator '{value}' is reserved for v0.2+; rendering as 'space'"
));
Separator::Space
}
_ => Separator::Literal(std::borrow::Cow::Owned(value.to_string())),
}
}
pub(super) fn validate_powerline_width(width: u16, warn: &mut impl FnMut(&str)) -> PowerlineWidth {
match width {
1 => PowerlineWidth::One,
2 => PowerlineWidth::Two,
other => {
warn(&format!(
"[layout_options].powerline_width = {other} is not 1 or 2; using 1"
));
PowerlineWidth::One
}
}
}