use serde::Serialize;
use strum::IntoEnumIterator;
use crate::diff::text::RenderOptions;
use crate::tui::components::diff_viewer::SINGLE_PANEL_THRESHOLD;
use crate::tui::components::help_modal::HELP_TEXT;
use crate::tui::display_columns::TAB_WIDTH;
use crate::tui::theme::{CustomPalette, OverlayTheme, PanelLayout};
use crate::tui::widgets::code_viewer::{DEFAULT_SYNTAX_THEME, syntax_theme_names};
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct ThemePayload {
pub id: String,
pub label: String,
pub palette: CustomPalette,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct SettingsPayload {
pub theme: OverlayTheme,
pub layout: PanelLayout,
pub node_highlight: bool,
pub render_options: RenderOptions,
pub syntax_theme: Option<String>,
pub custom_palette: CustomPalette,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct RenderOptionRow {
pub key: &'static str,
pub label: &'static str,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub struct Presets {
pub minimal: RenderOptions,
pub full: RenderOptions,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct StatePayload {
pub version: &'static str,
pub before: Option<String>,
pub after: Option<String>,
pub settings: SettingsPayload,
pub themes: Vec<ThemePayload>,
pub syntax_themes: Vec<String>,
pub default_syntax_theme: &'static str,
pub render_option_rows: Vec<RenderOptionRow>,
pub presets: Presets,
pub recent_pairs: Vec<[String; 2]>,
pub help_text: &'static str,
pub config_error: Option<String>,
pub single_panel_threshold: u16,
pub tab_width: usize,
pub review_on_start: bool,
}
pub fn render_option_rows() -> Vec<RenderOptionRow> {
const KEYS: [&str; 7] = [
"leading_whitespace",
"structural_punctuation",
"whole_pair_updates",
"paint_reindent_only_moves",
"paint_displaced_moves",
"paint_resized_moves",
"whole_identifier_updates",
];
KEYS.iter()
.zip(RenderOptions::FULL.options())
.map(|(key, (label, _))| RenderOptionRow { key, label })
.collect()
}
pub fn default_state() -> StatePayload {
let theme = OverlayTheme::default();
StatePayload {
version: env!("CARGO_PKG_VERSION"),
before: None,
after: None,
settings: SettingsPayload {
theme,
layout: PanelLayout::default(),
node_highlight: false,
render_options: RenderOptions::default(),
syntax_theme: None,
custom_palette: CustomPalette::from_palette(&theme.palette()),
},
themes: OverlayTheme::iter()
.map(|theme| ThemePayload {
id: serde_json::to_value(theme)
.ok()
.and_then(|value| value.as_str().map(str::to_string))
.unwrap_or_default(),
label: theme.to_string(),
palette: CustomPalette::from_palette(&theme.palette()),
})
.collect(),
syntax_themes: syntax_theme_names(),
default_syntax_theme: DEFAULT_SYNTAX_THEME,
render_option_rows: render_option_rows(),
presets: Presets {
minimal: RenderOptions::MINIMAL,
full: RenderOptions::FULL,
},
recent_pairs: Vec::new(),
help_text: HELP_TEXT,
config_error: None,
single_panel_threshold: SINGLE_PANEL_THRESHOLD,
tab_width: TAB_WIDTH,
review_on_start: false,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn render_option_rows_match_the_structs_own_serialization_order() {
let text = serde_json::to_string(&RenderOptions::FULL).unwrap();
let serialized: Vec<&str> = text.split('"').skip(1).step_by(2).collect();
let rows: Vec<&str> = render_option_rows().iter().map(|row| row.key).collect();
assert_eq!(rows, serialized);
assert!(render_option_rows()[1].label.starts_with("Structural"));
}
#[test]
fn the_state_names_every_theme_with_a_round_trippable_id() {
let state = default_state();
assert_eq!(state.themes.len(), OverlayTheme::iter().count());
for theme in &state.themes {
let parsed: OverlayTheme =
serde_json::from_value(serde_json::Value::String(theme.id.clone()))
.unwrap_or_else(|_| panic!("{} should parse back", theme.id));
assert_eq!(parsed.to_string(), theme.label);
}
assert!(
state
.syntax_themes
.iter()
.any(|name| name == DEFAULT_SYNTAX_THEME)
);
assert_eq!(state.single_panel_threshold, SINGLE_PANEL_THRESHOLD);
assert_eq!(state.tab_width, TAB_WIDTH);
assert!(state.help_text.contains("Navigation"));
}
}