style: format the whole tree with cargo fmt (stock defaults, #35)
One-time, mechanical reformat — no functional changes. The tree was not rustfmt-clean (~1800 hunks across ~100 files); this brings it to stock `cargo fmt` defaults so a `cargo fmt --check` CI gate can follow. Behaviour-preserving: 2509 pass / 0 fail / 1 ignored (unchanged baseline), clippy clean. A .git-blame-ignore-revs entry follows so `git blame` skips this commit.
This commit is contained in:
+210
-188
@@ -509,7 +509,10 @@ pub enum LoadPickerSubMode {
|
||||
/// Switched to via `b`. Same input/cursor surface as
|
||||
/// `PathEntryModal`; kept inline so the picker can flip
|
||||
/// back to List with `Esc`.
|
||||
PathEntry { input: String, cursor: usize },
|
||||
PathEntry {
|
||||
input: String,
|
||||
cursor: usize,
|
||||
},
|
||||
}
|
||||
|
||||
const PAGE_SCROLL_LINES: usize = 5;
|
||||
@@ -697,9 +700,7 @@ impl App {
|
||||
// `trimmed[1..].trim()`.
|
||||
let leading_ws = self.input.len() - self.input.trim_start().len();
|
||||
let mut offset = leading_ws + 1; // past the `:`
|
||||
while offset < self.input.len()
|
||||
&& self.input.as_bytes()[offset].is_ascii_whitespace()
|
||||
{
|
||||
while offset < self.input.len() && self.input.as_bytes()[offset].is_ascii_whitespace() {
|
||||
offset += 1;
|
||||
}
|
||||
let view = &self.input[offset..];
|
||||
@@ -727,8 +728,7 @@ impl App {
|
||||
pub fn input_validity_verdict(&self) -> Option<crate::dsl::walker::Severity> {
|
||||
let mode = match self.effective_mode() {
|
||||
EffectiveMode::Simple => Mode::Simple,
|
||||
EffectiveMode::AdvancedPersistent
|
||||
| EffectiveMode::AdvancedOneShot => Mode::Advanced,
|
||||
EffectiveMode::AdvancedPersistent | EffectiveMode::AdvancedOneShot => Mode::Advanced,
|
||||
};
|
||||
// Strip the `:` one-shot prefix so the walker verdicts the SQL
|
||||
// itself, not the escape marker (which it can't parse).
|
||||
@@ -1037,10 +1037,7 @@ impl App {
|
||||
Vec::new()
|
||||
}
|
||||
AppEvent::ExportSucceeded { path } => {
|
||||
self.note_system(crate::t!(
|
||||
"project.export_ok",
|
||||
path = path.display()
|
||||
));
|
||||
self.note_system(crate::t!("project.export_ok", path = path.display()));
|
||||
Vec::new()
|
||||
}
|
||||
AppEvent::ExportFailed { error } => {
|
||||
@@ -1056,11 +1053,7 @@ impl App {
|
||||
// `[ok] replay — N command(s)` summary is payload-bearing
|
||||
// (the count) and stays.
|
||||
self.mark_oldest_pending_echo(EchoStatus::Ok);
|
||||
self.note_system(crate::t!(
|
||||
"replay.completed",
|
||||
path = path,
|
||||
count = count
|
||||
));
|
||||
self.note_system(crate::t!("replay.completed", path = path, count = count));
|
||||
// ADR-0034: surface `[skip]` warnings for app-lifecycle
|
||||
// commands whose omission can leave the replayed state
|
||||
// incomplete (`import`, nested `replay`).
|
||||
@@ -1084,11 +1077,7 @@ impl App {
|
||||
// it, mirroring how the interactive `running: …`
|
||||
// path renders source-line context above an error.
|
||||
if line_number == 0 {
|
||||
self.note_error(crate::t!(
|
||||
"replay.failed_open",
|
||||
path = path,
|
||||
error = error
|
||||
));
|
||||
self.note_error(crate::t!("replay.failed_open", path = path, error = error));
|
||||
} else {
|
||||
self.note_error(crate::t!(
|
||||
"replay.failed_at_line",
|
||||
@@ -1097,10 +1086,7 @@ impl App {
|
||||
error = error
|
||||
));
|
||||
if !command.is_empty() {
|
||||
self.note_error(crate::t!(
|
||||
"replay.command_echo",
|
||||
command = command
|
||||
));
|
||||
self.note_error(crate::t!("replay.command_echo", command = command));
|
||||
}
|
||||
}
|
||||
Vec::new()
|
||||
@@ -1237,8 +1223,7 @@ impl App {
|
||||
// stays F1-only.
|
||||
let hint_key = key.code == KeyCode::F(1)
|
||||
|| (self.demo_mode
|
||||
&& (key.code, key.modifiers)
|
||||
== (KeyCode::Char('g'), KeyModifiers::CONTROL));
|
||||
&& (key.code, key.modifiers) == (KeyCode::Char('g'), KeyModifiers::CONTROL));
|
||||
if hint_key {
|
||||
if self.input.trim().is_empty() {
|
||||
self.note_hint_for_recent_error();
|
||||
@@ -1369,8 +1354,8 @@ impl App {
|
||||
/// against crossterm 0.29). Only active in demo mode (the caller
|
||||
/// gates on `self.demo_mode`).
|
||||
fn handle_demo_caption_key(&mut self, key: KeyEvent) -> Option<Vec<Action>> {
|
||||
let is_toggle = key.code == KeyCode::Char('5')
|
||||
&& key.modifiers.contains(KeyModifiers::CONTROL);
|
||||
let is_toggle =
|
||||
key.code == KeyCode::Char('5') && key.modifiers.contains(KeyModifiers::CONTROL);
|
||||
|
||||
if self.demo_caption_capturing {
|
||||
if is_toggle {
|
||||
@@ -1379,8 +1364,7 @@ impl App {
|
||||
self.demo_caption_capturing = false;
|
||||
let text = std::mem::take(&mut self.demo_caption_buffer);
|
||||
let trimmed = text.trim();
|
||||
self.demo_caption =
|
||||
(!trimmed.is_empty()).then(|| trimmed.to_string());
|
||||
self.demo_caption = (!trimmed.is_empty()).then(|| trimmed.to_string());
|
||||
} else {
|
||||
match key.code {
|
||||
// Plain characters accumulate invisibly; the prompt
|
||||
@@ -1553,7 +1537,10 @@ impl App {
|
||||
&self.schema_cache,
|
||||
self.effective_mode().as_mode(),
|
||||
)?;
|
||||
comp.replaced_range = (comp.replaced_range.0 + offset, comp.replaced_range.1 + offset);
|
||||
comp.replaced_range = (
|
||||
comp.replaced_range.0 + offset,
|
||||
comp.replaced_range.1 + offset,
|
||||
);
|
||||
Some(comp)
|
||||
}
|
||||
|
||||
@@ -1581,8 +1568,7 @@ impl App {
|
||||
idx: usize,
|
||||
) -> crate::completion::LastCompletion {
|
||||
let inserted = comp.candidates[idx].text.clone();
|
||||
let original_text =
|
||||
self.input[comp.replaced_range.0..comp.replaced_range.1].to_string();
|
||||
let original_text = self.input[comp.replaced_range.0..comp.replaced_range.1].to_string();
|
||||
self.input
|
||||
.replace_range(comp.replaced_range.0..comp.replaced_range.1, &inserted);
|
||||
let new_end = comp.replaced_range.0 + inserted.len();
|
||||
@@ -1777,7 +1763,10 @@ impl App {
|
||||
// teaching echo (ADR-0038) on an advanced effective mode.
|
||||
let (submission_mode, effective_input) =
|
||||
if self.mode == Mode::Simple && trimmed.starts_with(':') {
|
||||
(EffectiveMode::AdvancedOneShot, trimmed[1..].trim().to_string())
|
||||
(
|
||||
EffectiveMode::AdvancedOneShot,
|
||||
trimmed[1..].trim().to_string(),
|
||||
)
|
||||
} else if self.mode == Mode::Advanced {
|
||||
(EffectiveMode::AdvancedPersistent, trimmed.to_string())
|
||||
} else {
|
||||
@@ -1845,11 +1834,7 @@ impl App {
|
||||
/// simple and advanced modes; the parse-first refactor
|
||||
/// (round-5) routes app commands here before the
|
||||
/// mode-specific DSL/SQL paths.
|
||||
fn dispatch_app_command(
|
||||
&mut self,
|
||||
cmd: crate::dsl::AppCommand,
|
||||
source: &str,
|
||||
) -> Vec<Action> {
|
||||
fn dispatch_app_command(&mut self, cmd: crate::dsl::AppCommand, source: &str) -> Vec<Action> {
|
||||
use crate::dsl::{AppCommand, MessagesValue, ModeValue};
|
||||
debug!(command = ?cmd, "dispatch app command");
|
||||
match cmd {
|
||||
@@ -2009,11 +1994,8 @@ impl App {
|
||||
// mode so the walker gates SQL-only forms — simple-mode
|
||||
// `select` returns the "this is SQL" hint as a normal
|
||||
// parse error and is rendered through the Err arm below.
|
||||
match crate::dsl::parser::parse_command_with_schema_in_mode(
|
||||
input,
|
||||
&self.schema_cache,
|
||||
mode,
|
||||
) {
|
||||
match crate::dsl::parser::parse_command_with_schema_in_mode(input, &self.schema_cache, mode)
|
||||
{
|
||||
Ok(Command::Replay { path }) => {
|
||||
// `replay` is parsed as a DSL command for the
|
||||
// sake of grammar uniformity, but its execution
|
||||
@@ -2127,15 +2109,9 @@ impl App {
|
||||
.get(..*position)
|
||||
.map_or(*position, |s| s.chars().count());
|
||||
let pad = prefix.chars().count() + chars_before;
|
||||
self.note_error(crate::t!(
|
||||
"parse.caret",
|
||||
padding = " ".repeat(pad)
|
||||
));
|
||||
self.note_error(crate::t!("parse.caret", padding = " ".repeat(pad)));
|
||||
}
|
||||
self.note_error(crate::t!(
|
||||
"parse.error",
|
||||
detail = parse_error_message(&err)
|
||||
));
|
||||
self.note_error(crate::t!("parse.error", detail = parse_error_message(&err)));
|
||||
// ADR-0033 Amendment 3: combine the DSL error with a
|
||||
// pointer to advanced mode when the same line would
|
||||
// run as SQL there. Only in simple mode (a one-shot
|
||||
@@ -2228,7 +2204,11 @@ impl App {
|
||||
| Command::AddRelationship { .. }
|
||||
| Command::DropRelationship { .. }
|
||||
) {
|
||||
debug!(verb = command.verb(), width = self.last_output_width, "render: relationship diagrams (ADR-0044)");
|
||||
debug!(
|
||||
verb = command.verb(),
|
||||
width = self.last_output_width,
|
||||
"render: relationship diagrams (ADR-0044)"
|
||||
);
|
||||
for line in crate::output_render::render_structure_with_diagrams(
|
||||
desc,
|
||||
self.last_output_width,
|
||||
@@ -2252,11 +2232,7 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_dsl_explain_success(
|
||||
&mut self,
|
||||
command: &Command,
|
||||
plan: &crate::db::QueryPlan,
|
||||
) {
|
||||
fn handle_dsl_explain_success(&mut self, command: &Command, plan: &crate::db::QueryPlan) {
|
||||
self.note_ok_summary(command);
|
||||
// ADR-0028 §3: the display SQL, then the plan tree.
|
||||
// `render_explain_plan` returns ready-built `OutputLine`s
|
||||
@@ -2350,11 +2326,7 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_dsl_add_column_success(
|
||||
&mut self,
|
||||
command: &Command,
|
||||
result: AddColumnResult,
|
||||
) {
|
||||
fn handle_dsl_add_column_success(&mut self, command: &Command, result: AddColumnResult) {
|
||||
self.note_ok_summary(command);
|
||||
// ADR-0018 §9 / ADR-0038 §6 category 3: emit auto-fill note(s)
|
||||
// before the structure render so the pedagogical "the tool did
|
||||
@@ -2369,19 +2341,12 @@ impl App {
|
||||
self.current_table = Some(result.description);
|
||||
}
|
||||
|
||||
fn handle_dsl_drop_column_success(
|
||||
&mut self,
|
||||
command: &Command,
|
||||
result: DropColumnResult,
|
||||
) {
|
||||
fn handle_dsl_drop_column_success(&mut self, command: &Command, result: DropColumnResult) {
|
||||
self.note_ok_summary(command);
|
||||
// ADR-0025: when `--cascade` removed covering indexes,
|
||||
// name each one so the learner sees the side effect.
|
||||
for index in &result.dropped_indexes {
|
||||
self.note_system(crate::t!(
|
||||
"ok.index_dropped_with_column",
|
||||
index = index,
|
||||
));
|
||||
self.note_system(crate::t!("ok.index_dropped_with_column", index = index,));
|
||||
}
|
||||
for line in crate::output_render::render_structure(&result.description) {
|
||||
self.note_system(line);
|
||||
@@ -2415,10 +2380,7 @@ impl App {
|
||||
lossy = note.lossy
|
||||
)
|
||||
} else {
|
||||
crate::t!(
|
||||
"client_side.transformed",
|
||||
count = note.transformed
|
||||
)
|
||||
crate::t!("client_side.transformed", count = note.transformed)
|
||||
};
|
||||
self.push_category_three_prose(line);
|
||||
}
|
||||
@@ -2583,9 +2545,7 @@ impl App {
|
||||
(Operation::RenameTable, Some(table.as_str()), None)
|
||||
}
|
||||
},
|
||||
C::SqlCreateTable { name, .. } => {
|
||||
(Operation::CreateTable, Some(name.as_str()), None)
|
||||
}
|
||||
C::SqlCreateTable { name, .. } => (Operation::CreateTable, Some(name.as_str()), None),
|
||||
C::DropTable { name } => (Operation::DropTable, Some(name.as_str()), None),
|
||||
C::SqlDropTable { name, .. } => (Operation::DropTable, Some(name.as_str()), None),
|
||||
C::AddColumn { table, column, .. } => (
|
||||
@@ -2635,9 +2595,7 @@ impl App {
|
||||
// SQL `CREATE [UNIQUE] INDEX` shares the add-index operation
|
||||
// (it reuses `do_add_index`); route engine/validation errors
|
||||
// through it with the parsed table.
|
||||
C::SqlCreateIndex { table, .. } => {
|
||||
(Operation::AddIndex, Some(table.as_str()), None)
|
||||
}
|
||||
C::SqlCreateIndex { table, .. } => (Operation::AddIndex, Some(table.as_str()), None),
|
||||
C::AddConstraint { table, column, .. } => (
|
||||
Operation::AddConstraint,
|
||||
Some(table.as_str()),
|
||||
@@ -2700,19 +2658,13 @@ impl App {
|
||||
// `dispatch_input` routes them through
|
||||
// `dispatch_app_command` before the DSL execution
|
||||
// pipeline that this context builder feeds.
|
||||
C::App(_) => unreachable!(
|
||||
"App commands are dispatched before reaching dsl execution"
|
||||
),
|
||||
C::App(_) => unreachable!("App commands are dispatched before reaching dsl execution"),
|
||||
};
|
||||
|
||||
TranslateContext {
|
||||
operation: Some(operation),
|
||||
table: facts
|
||||
.table
|
||||
.or_else(|| fallback_table.map(str::to_string)),
|
||||
column: facts
|
||||
.column
|
||||
.or_else(|| fallback_column.map(str::to_string)),
|
||||
table: facts.table.or_else(|| fallback_table.map(str::to_string)),
|
||||
column: facts.column.or_else(|| fallback_column.map(str::to_string)),
|
||||
child_table: facts.child_table,
|
||||
parent_table: facts.parent_table,
|
||||
parent_column: facts.parent_column,
|
||||
@@ -2818,11 +2770,7 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_path_entry_key(
|
||||
&mut self,
|
||||
key: KeyEvent,
|
||||
mut state: PathEntryModal,
|
||||
) -> Vec<Action> {
|
||||
fn handle_path_entry_key(&mut self, key: KeyEvent, mut state: PathEntryModal) -> Vec<Action> {
|
||||
match key.code {
|
||||
KeyCode::Esc => {
|
||||
self.modal = None;
|
||||
@@ -2904,11 +2852,7 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_load_picker_key(
|
||||
&mut self,
|
||||
key: KeyEvent,
|
||||
mut state: LoadPickerModal,
|
||||
) -> Vec<Action> {
|
||||
fn handle_load_picker_key(&mut self, key: KeyEvent, mut state: LoadPickerModal) -> Vec<Action> {
|
||||
match &mut state.sub_mode {
|
||||
LoadPickerSubMode::List => match key.code {
|
||||
KeyCode::Esc => {
|
||||
@@ -3198,7 +3142,10 @@ impl App {
|
||||
.map(|c| c.text.clone())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
self.push_category_three_prose(crate::t!("hint.ambient_expected", expected = names));
|
||||
self.push_category_three_prose(crate::t!(
|
||||
"hint.ambient_expected",
|
||||
expected = names
|
||||
));
|
||||
}
|
||||
None => self.note_getting_started(),
|
||||
}
|
||||
@@ -3413,10 +3360,7 @@ fn render_usage_block(input: &str, mode: Mode) -> String {
|
||||
.into_iter()
|
||||
.map(|w| format!("`{w}`"))
|
||||
.collect();
|
||||
crate::t!(
|
||||
"parse.available_commands",
|
||||
commands = names.join(", ")
|
||||
)
|
||||
crate::t!("parse.available_commands", commands = names.join(", "))
|
||||
}
|
||||
|
||||
fn render_cascade_effect(effect: &CascadeEffect) -> String {
|
||||
@@ -3424,9 +3368,7 @@ fn render_cascade_effect(effect: &CascadeEffect) -> String {
|
||||
let action_key = match effect.action {
|
||||
ReferentialAction::Cascade => "db.cascade.action_deleted",
|
||||
ReferentialAction::SetNull => "db.cascade.action_set_null",
|
||||
ReferentialAction::Restrict | ReferentialAction::NoAction => {
|
||||
"db.cascade.action_blocked"
|
||||
}
|
||||
ReferentialAction::Restrict | ReferentialAction::NoAction => "db.cascade.action_blocked",
|
||||
};
|
||||
crate::t!(
|
||||
"db.cascade.summary",
|
||||
@@ -3464,7 +3406,10 @@ mod tests {
|
||||
fn demo_badge_label_maps_the_invisible_keys() {
|
||||
let none = KeyModifiers::NONE;
|
||||
assert_eq!(demo_badge_label(&ke(KeyCode::Tab, none)), Some("[TAB]"));
|
||||
assert_eq!(demo_badge_label(&ke(KeyCode::BackTab, KeyModifiers::SHIFT)), Some("[SHIFT-TAB]"));
|
||||
assert_eq!(
|
||||
demo_badge_label(&ke(KeyCode::BackTab, KeyModifiers::SHIFT)),
|
||||
Some("[SHIFT-TAB]")
|
||||
);
|
||||
assert_eq!(demo_badge_label(&ke(KeyCode::Enter, none)), Some("[ENTER]"));
|
||||
assert_eq!(demo_badge_label(&ke(KeyCode::Esc, none)), Some("[ESC]"));
|
||||
assert_eq!(demo_badge_label(&ke(KeyCode::Up, none)), Some("[UP]"));
|
||||
@@ -3474,8 +3419,14 @@ mod tests {
|
||||
assert_eq!(demo_badge_label(&ke(KeyCode::Home, none)), Some("[HOME]"));
|
||||
assert_eq!(demo_badge_label(&ke(KeyCode::End, none)), Some("[END]"));
|
||||
assert_eq!(demo_badge_label(&ke(KeyCode::PageUp, none)), Some("[PGUP]"));
|
||||
assert_eq!(demo_badge_label(&ke(KeyCode::PageDown, none)), Some("[PGDN]"));
|
||||
assert_eq!(demo_badge_label(&ke(KeyCode::Backspace, none)), Some("[BKSP]"));
|
||||
assert_eq!(
|
||||
demo_badge_label(&ke(KeyCode::PageDown, none)),
|
||||
Some("[PGDN]")
|
||||
);
|
||||
assert_eq!(
|
||||
demo_badge_label(&ke(KeyCode::Backspace, none)),
|
||||
Some("[BKSP]")
|
||||
);
|
||||
assert_eq!(demo_badge_label(&ke(KeyCode::Delete, none)), Some("[DEL]"));
|
||||
assert_eq!(
|
||||
demo_badge_label(&ke(KeyCode::Char('o'), KeyModifiers::CONTROL)),
|
||||
@@ -3493,12 +3444,24 @@ mod tests {
|
||||
#[test]
|
||||
fn demo_badge_label_none_for_glyphs_and_excluded_chords() {
|
||||
// Plain characters render their own glyph — no badge.
|
||||
assert_eq!(demo_badge_label(&ke(KeyCode::Char('a'), KeyModifiers::NONE)), None);
|
||||
assert_eq!(demo_badge_label(&ke(KeyCode::Char(' '), KeyModifiers::NONE)), None);
|
||||
assert_eq!(
|
||||
demo_badge_label(&ke(KeyCode::Char('a'), KeyModifiers::NONE)),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
demo_badge_label(&ke(KeyCode::Char(' '), KeyModifiers::NONE)),
|
||||
None
|
||||
);
|
||||
// Quit and the (Phase C) caption toggle are deliberately excluded.
|
||||
assert_eq!(demo_badge_label(&ke(KeyCode::Char('c'), KeyModifiers::CONTROL)), None);
|
||||
assert_eq!(
|
||||
demo_badge_label(&ke(KeyCode::Char('c'), KeyModifiers::CONTROL)),
|
||||
None
|
||||
);
|
||||
// Ctrl+] decodes to Char('5')+CONTROL — must not badge.
|
||||
assert_eq!(demo_badge_label(&ke(KeyCode::Char('5'), KeyModifiers::CONTROL)), None);
|
||||
assert_eq!(
|
||||
demo_badge_label(&ke(KeyCode::Char('5'), KeyModifiers::CONTROL)),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -3606,7 +3569,10 @@ mod tests {
|
||||
assert!(app.demo_caption_capturing, "still capturing");
|
||||
assert_eq!(app.demo_caption_buffer, "note");
|
||||
assert_eq!(app.input, "");
|
||||
assert_eq!(app.demo_badge, None, "inert keys raise no badge while capturing");
|
||||
assert_eq!(
|
||||
app.demo_badge, None,
|
||||
"inert keys raise no badge while capturing"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4211,7 +4177,9 @@ mod tests {
|
||||
type_str(&mut app, "copy sideways");
|
||||
let actions = submit(&mut app);
|
||||
assert!(
|
||||
!actions.iter().any(|a| matches!(a, Action::CopyToClipboard(_))),
|
||||
!actions
|
||||
.iter()
|
||||
.any(|a| matches!(a, Action::CopyToClipboard(_))),
|
||||
"an unknown target does not copy",
|
||||
);
|
||||
let rendered = app
|
||||
@@ -4399,7 +4367,10 @@ mod tests {
|
||||
);
|
||||
// … names the table's columns so the user can see what's needed …
|
||||
assert!(
|
||||
out.contains("Name") && out.contains("Age") && out.contains("id") && out.contains("SerNo"),
|
||||
out.contains("Name")
|
||||
&& out.contains("Age")
|
||||
&& out.contains("id")
|
||||
&& out.contains("SerNo"),
|
||||
"missing the column-name list in: {out}",
|
||||
);
|
||||
// … and shows the column-list override targeting the non-auto columns.
|
||||
@@ -4423,9 +4394,15 @@ mod tests {
|
||||
let _ = submit(&mut app);
|
||||
let out = error_lines(&app);
|
||||
// The teaching line names the user-supplied columns …
|
||||
assert!(out.contains("Name") && out.contains("Age"), "missing non-auto column names in: {out}");
|
||||
assert!(
|
||||
out.contains("Name") && out.contains("Age"),
|
||||
"missing non-auto column names in: {out}"
|
||||
);
|
||||
// … the auto-generated columns …
|
||||
assert!(out.contains("id") && out.contains("SerNo"), "missing auto column names in: {out}");
|
||||
assert!(
|
||||
out.contains("id") && out.contains("SerNo"),
|
||||
"missing auto column names in: {out}"
|
||||
);
|
||||
// … signals the contract …
|
||||
assert!(
|
||||
out.contains("auto-generated"),
|
||||
@@ -4520,10 +4497,7 @@ mod tests {
|
||||
let mut app = App::new();
|
||||
install_customers_schema_two_serials(&mut app);
|
||||
app.mode = Mode::Advanced;
|
||||
type_str(
|
||||
&mut app,
|
||||
"insert into Customers values (13, 'Oli', 42, 13)",
|
||||
);
|
||||
type_str(&mut app, "insert into Customers values (13, 'Oli', 42, 13)");
|
||||
let actions = submit(&mut app);
|
||||
assert!(
|
||||
actions
|
||||
@@ -4552,7 +4526,9 @@ mod tests {
|
||||
type_str(&mut app, "insert into Customers values ('Oli', 52, 3)");
|
||||
let actions = submit(&mut app);
|
||||
assert!(
|
||||
!actions.iter().any(|a| matches!(a, Action::ExecuteDsl { .. })),
|
||||
!actions
|
||||
.iter()
|
||||
.any(|a| matches!(a, Action::ExecuteDsl { .. })),
|
||||
"simple-mode Form B count mismatch must NOT dispatch; got: {actions:?}",
|
||||
);
|
||||
}
|
||||
@@ -4565,7 +4541,9 @@ mod tests {
|
||||
type_str(&mut app, "insert into Customers values ('Oli')");
|
||||
let actions = submit(&mut app);
|
||||
assert!(
|
||||
!actions.iter().any(|a| matches!(a, Action::ExecuteDsl { .. })),
|
||||
!actions
|
||||
.iter()
|
||||
.any(|a| matches!(a, Action::ExecuteDsl { .. })),
|
||||
"simple-mode Form B under-supply must NOT dispatch; got: {actions:?}",
|
||||
);
|
||||
}
|
||||
@@ -4580,7 +4558,9 @@ mod tests {
|
||||
type_str(&mut app, "insert into Customers (Name, Age) values ('Oli')");
|
||||
let actions = submit(&mut app);
|
||||
assert!(
|
||||
!actions.iter().any(|a| matches!(a, Action::ExecuteDsl { .. })),
|
||||
!actions
|
||||
.iter()
|
||||
.any(|a| matches!(a, Action::ExecuteDsl { .. })),
|
||||
"simple-mode Form A count mismatch must NOT dispatch; got: {actions:?}",
|
||||
);
|
||||
}
|
||||
@@ -4616,9 +4596,7 @@ mod tests {
|
||||
for c in &tc {
|
||||
app.schema_cache.columns.push(c.name.clone());
|
||||
}
|
||||
app.schema_cache
|
||||
.table_columns
|
||||
.insert("T".to_string(), tc);
|
||||
app.schema_cache.table_columns.insert("T".to_string(), tc);
|
||||
type_str(&mut app, "insert into T values (1, 2), (3, 4)");
|
||||
let actions = submit(&mut app);
|
||||
assert!(
|
||||
@@ -4649,11 +4627,11 @@ mod tests {
|
||||
// advanced-mode hint at all, so we look for any line carrying
|
||||
// the "mode advanced" actionable fragment that the pointer
|
||||
// always emits.
|
||||
let has_pointer = app
|
||||
.output
|
||||
.iter()
|
||||
.any(|l| l.text.contains("mode advanced"));
|
||||
assert!(!has_pointer, "unknown command must not point at advanced mode");
|
||||
let has_pointer = app.output.iter().any(|l| l.text.contains("mode advanced"));
|
||||
assert!(
|
||||
!has_pointer,
|
||||
"unknown command must not point at advanced mode"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4702,7 +4680,11 @@ mod tests {
|
||||
app.mode = mode;
|
||||
type_str(&mut app, input);
|
||||
match submit(&mut app).as_slice() {
|
||||
[Action::ExecuteDsl { submission_mode, .. }] => *submission_mode,
|
||||
[
|
||||
Action::ExecuteDsl {
|
||||
submission_mode, ..
|
||||
},
|
||||
] => *submission_mode,
|
||||
other => panic!("expected one ExecuteDsl; got {other:?}"),
|
||||
}
|
||||
};
|
||||
@@ -4733,7 +4715,9 @@ mod tests {
|
||||
app.update(AppEvent::DslSucceeded {
|
||||
command: cmd.clone(),
|
||||
description: None,
|
||||
echo: Some(vec!["CREATE TABLE Other (id serial PRIMARY KEY)".to_string()]),
|
||||
echo: Some(vec![
|
||||
"CREATE TABLE Other (id serial PRIMARY KEY)".to_string(),
|
||||
]),
|
||||
});
|
||||
let texts: Vec<&str> = app.output.iter().map(|l| l.text.as_str()).collect();
|
||||
// ADR-0040: no `[ok]` summary; with no preceding `running:` echo
|
||||
@@ -4792,7 +4776,10 @@ mod tests {
|
||||
.position(|t| t.contains("Executing SQL:"))
|
||||
.expect("an echo line");
|
||||
assert_eq!(echo_idx, 0, "teaching echo leads the output: {texts:?}");
|
||||
assert!(texts[echo_idx].contains(expected), "echo carries the SQL: {texts:?}");
|
||||
assert!(
|
||||
texts[echo_idx].contains(expected),
|
||||
"echo carries the SQL: {texts:?}"
|
||||
);
|
||||
// ADR-0038 §4 polish: every success arm now wires the echo as
|
||||
// `OutputKind::TeachingEcho` so `ui::render_output_line` fires
|
||||
// the dim-prefix + advanced-lex custom branch. Pinning this
|
||||
@@ -4911,7 +4898,9 @@ mod tests {
|
||||
description: sample_description("T"),
|
||||
client_side: None,
|
||||
},
|
||||
echo: Some(vec!["ALTER TABLE T ALTER COLUMN c SET DATA TYPE text".to_string()]),
|
||||
echo: Some(vec![
|
||||
"ALTER TABLE T ALTER COLUMN c SET DATA TYPE text".to_string(),
|
||||
]),
|
||||
dont_convert_caveat: false,
|
||||
});
|
||||
assert_echo_beneath_ok(&app, "ALTER TABLE T ALTER COLUMN c SET DATA TYPE text");
|
||||
@@ -5126,9 +5115,7 @@ mod tests {
|
||||
dont_convert_caveat: false,
|
||||
});
|
||||
assert!(
|
||||
!app.output
|
||||
.iter()
|
||||
.any(|l| l.text.contains("--dont-convert")),
|
||||
!app.output.iter().any(|l| l.text.contains("--dont-convert")),
|
||||
"no caveat in simple mode (no echo to refer to)",
|
||||
);
|
||||
}
|
||||
@@ -5198,7 +5185,10 @@ mod tests {
|
||||
);
|
||||
// Pin the `Executing SQL:` prefix repeats once per statement
|
||||
// (the plain-rendering shape until the styled-runs polish lands).
|
||||
let exec_count = texts.iter().filter(|t| t.contains("Executing SQL:")).count();
|
||||
let exec_count = texts
|
||||
.iter()
|
||||
.filter(|t| t.contains("Executing SQL:"))
|
||||
.count();
|
||||
assert_eq!(exec_count, 3, "one Executing SQL: per statement: {texts:?}");
|
||||
}
|
||||
|
||||
@@ -5223,19 +5213,13 @@ mod tests {
|
||||
// (could be the caret line, the parse-error detail
|
||||
// line, or the usage line). Scan for the friendly
|
||||
// "unknown mode" anchor phrase.
|
||||
let anywhere = app
|
||||
.output
|
||||
.iter()
|
||||
.any(|l| l.text.contains("unknown mode"));
|
||||
let anywhere = app.output.iter().any(|l| l.text.contains("unknown mode"));
|
||||
assert!(
|
||||
anywhere,
|
||||
"expected 'unknown mode' somewhere in output: {:?}",
|
||||
app.output.iter().map(|l| &l.text).collect::<Vec<_>>(),
|
||||
);
|
||||
let any_error = app
|
||||
.output
|
||||
.iter()
|
||||
.any(|l| l.kind == OutputKind::Error);
|
||||
let any_error = app.output.iter().any(|l| l.kind == OutputKind::Error);
|
||||
assert!(any_error, "expected at least one Error line");
|
||||
}
|
||||
|
||||
@@ -5325,11 +5309,17 @@ mod tests {
|
||||
app.schema_cache.tables = vec!["Orders".into(), "Customers".into()];
|
||||
app.schema_cache.table_columns.insert(
|
||||
"Orders".into(),
|
||||
vec![TableColumn::new("id", Type::Serial), TableColumn::new("customer_id", Type::Int)],
|
||||
vec![
|
||||
TableColumn::new("id", Type::Serial),
|
||||
TableColumn::new("customer_id", Type::Int),
|
||||
],
|
||||
);
|
||||
app.schema_cache.table_columns.insert(
|
||||
"Customers".into(),
|
||||
vec![TableColumn::new("id", Type::Serial), TableColumn::new("name", Type::Text)],
|
||||
vec![
|
||||
TableColumn::new("id", Type::Serial),
|
||||
TableColumn::new("name", Type::Text),
|
||||
],
|
||||
);
|
||||
for t in app.schema_cache.tables.clone() {
|
||||
for c in &app.schema_cache.table_columns[&t] {
|
||||
@@ -5490,10 +5480,7 @@ mod tests {
|
||||
detail: "SCAN Customers".to_string(),
|
||||
}],
|
||||
};
|
||||
app.update(AppEvent::DslExplainSucceeded {
|
||||
command: cmd,
|
||||
plan,
|
||||
});
|
||||
app.update(AppEvent::DslExplainSucceeded { command: cmd, plan });
|
||||
// ADR-0040: no `[ok] explain …` header — the (no-echo here)
|
||||
// command's success shows via the marker; the plan output
|
||||
// itself carries the content.
|
||||
@@ -5549,7 +5536,11 @@ mod tests {
|
||||
.iter()
|
||||
.find(|l| l.kind == OutputKind::Echo)
|
||||
.expect("dispatch pushed an echo");
|
||||
assert_eq!(echo.status, Some(EchoStatus::Pending), "pending before result");
|
||||
assert_eq!(
|
||||
echo.status,
|
||||
Some(EchoStatus::Pending),
|
||||
"pending before result"
|
||||
);
|
||||
app.update(AppEvent::DslSucceeded {
|
||||
command: Command::CreateTable {
|
||||
name: "T".to_string(),
|
||||
@@ -5639,8 +5630,14 @@ mod tests {
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
assert!(text.contains("[ok] replay"), "summary present:\n{text}");
|
||||
assert!(text.contains("import a.zip"), "import skip warning rendered:\n{text}");
|
||||
assert!(text.contains("nested `replay x`"), "nested-replay skip warning rendered:\n{text}");
|
||||
assert!(
|
||||
text.contains("import a.zip"),
|
||||
"import skip warning rendered:\n{text}"
|
||||
);
|
||||
assert!(
|
||||
text.contains("nested `replay x`"),
|
||||
"nested-replay skip warning rendered:\n{text}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -5736,7 +5733,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn hint_command_parses_to_app_hint() {
|
||||
use crate::dsl::{parse_command, AppCommand, Command};
|
||||
use crate::dsl::{AppCommand, Command, parse_command};
|
||||
assert!(matches!(
|
||||
parse_command("hint"),
|
||||
Ok(Command::App(AppCommand::Hint))
|
||||
@@ -5747,7 +5744,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn version_command_parses_to_app_version() {
|
||||
use crate::dsl::{parse_command, AppCommand, Command};
|
||||
use crate::dsl::{AppCommand, Command, parse_command};
|
||||
assert!(matches!(
|
||||
parse_command("version"),
|
||||
Ok(Command::App(AppCommand::Version))
|
||||
@@ -5801,7 +5798,10 @@ mod tests {
|
||||
let mut app = App::new();
|
||||
type_str(&mut app, "show ");
|
||||
app.update(key(KeyCode::Tab));
|
||||
assert!(app.last_completion.is_some(), "precondition: Tab sets the memo");
|
||||
assert!(
|
||||
app.last_completion.is_some(),
|
||||
"precondition: Tab sets the memo"
|
||||
);
|
||||
let input = app.input.clone();
|
||||
f1(&mut app);
|
||||
assert!(app.last_completion.is_some(), "F1 must not clear the memo");
|
||||
@@ -5827,8 +5827,14 @@ mod tests {
|
||||
let input = app.input.clone();
|
||||
let before = app.output.len();
|
||||
app.update(ctrl_g());
|
||||
assert_eq!(app.input, input, "Ctrl-G must not change the buffer (no `g` typed)");
|
||||
assert!(app.output.len() > before, "Ctrl-G must emit the same hint F1 does");
|
||||
assert_eq!(
|
||||
app.input, input,
|
||||
"Ctrl-G must not change the buffer (no `g` typed)"
|
||||
);
|
||||
assert!(
|
||||
app.output.len() > before,
|
||||
"Ctrl-G must emit the same hint F1 does"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -5855,7 +5861,11 @@ mod tests {
|
||||
let before = app.output.len();
|
||||
app.update(ctrl_g());
|
||||
assert_eq!(app.input, input, "Ctrl-G must not insert a `g`");
|
||||
assert_eq!(app.output.len(), before, "Ctrl-G does nothing when demo mode is off");
|
||||
assert_eq!(
|
||||
app.output.len(),
|
||||
before,
|
||||
"Ctrl-G does nothing when demo mode is off"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -5949,7 +5959,10 @@ mod tests {
|
||||
#[test]
|
||||
fn f1_on_add_relationship_renders_the_relationship_block() {
|
||||
let mut app = App::new();
|
||||
type_str(&mut app, "add 1:n relationship from Customers.id to Orders.cust ");
|
||||
type_str(
|
||||
&mut app,
|
||||
"add 1:n relationship from Customers.id to Orders.cust ",
|
||||
);
|
||||
f1(&mut app);
|
||||
assert!(
|
||||
output_contains(&app, "one parent, many children"),
|
||||
@@ -6142,14 +6155,8 @@ mod tests {
|
||||
let mut app = App::new();
|
||||
let cmd = Command::Update {
|
||||
table: "Customers".to_string(),
|
||||
assignments: vec![(
|
||||
"id".to_string(),
|
||||
crate::dsl::Value::Number("7".to_string()),
|
||||
)],
|
||||
filter: crate::dsl::RowFilter::eq(
|
||||
"name",
|
||||
crate::dsl::Value::Text("Bob".to_string()),
|
||||
),
|
||||
assignments: vec![("id".to_string(), crate::dsl::Value::Number("7".to_string()))],
|
||||
filter: crate::dsl::RowFilter::eq("name", crate::dsl::Value::Text("Bob".to_string())),
|
||||
};
|
||||
let err = crate::db::DbError::Sqlite {
|
||||
message: "UNIQUE constraint failed: Customers.id".to_string(),
|
||||
@@ -6709,7 +6716,10 @@ mod tests {
|
||||
app.update(key(KeyCode::Backspace));
|
||||
let actions = app.update(key(KeyCode::Enter));
|
||||
assert_eq!(app.input, "select", "input untouched in navigation mode");
|
||||
assert!(actions.is_empty(), "Enter does not submit in navigation mode");
|
||||
assert!(
|
||||
actions.is_empty(),
|
||||
"Enter does not submit in navigation mode"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -6720,7 +6730,10 @@ mod tests {
|
||||
app.update(key(KeyCode::Down));
|
||||
app.update(key(KeyCode::Down));
|
||||
assert_eq!(app.tables_scroll, 2);
|
||||
assert_eq!(app.relationships_scroll, 0, "only the focused panel scrolls");
|
||||
assert_eq!(
|
||||
app.relationships_scroll, 0,
|
||||
"only the focused panel scrolls"
|
||||
);
|
||||
app.update(key(KeyCode::Up));
|
||||
assert_eq!(app.tables_scroll, 1);
|
||||
// Up saturates at the top.
|
||||
@@ -6998,7 +7011,8 @@ mod tests {
|
||||
for round in 0..3 {
|
||||
app.update(key(KeyCode::Up));
|
||||
assert_eq!(
|
||||
app.input, "insert into Thing values (1)",
|
||||
app.input,
|
||||
"insert into Thing values (1)",
|
||||
"Up #{} should recall the newest entry",
|
||||
round + 1,
|
||||
);
|
||||
@@ -7220,8 +7234,7 @@ mod tests {
|
||||
has_default: false,
|
||||
}],
|
||||
);
|
||||
app.input =
|
||||
"select * from products where price like 5".to_string();
|
||||
app.input = "select * from products where price like 5".to_string();
|
||||
assert_eq!(
|
||||
app.input_validity_verdict(),
|
||||
Some(crate::dsl::walker::Severity::Warning),
|
||||
@@ -7350,8 +7363,10 @@ mod tests {
|
||||
"directly-deleted count surfaced: {texts:?}",
|
||||
);
|
||||
assert!(
|
||||
texts.iter().any(|t| t.contains("2 row(s) deleted in `Orders`")
|
||||
&& t.contains("relationship `places`")),
|
||||
texts
|
||||
.iter()
|
||||
.any(|t| t.contains("2 row(s) deleted in `Orders`")
|
||||
&& t.contains("relationship `places`")),
|
||||
"per-relationship cascade summary surfaced: {texts:?}",
|
||||
);
|
||||
}
|
||||
@@ -7386,11 +7401,15 @@ mod tests {
|
||||
});
|
||||
let texts: Vec<String> = app.output.iter().map(|l| l.text.clone()).collect();
|
||||
assert!(
|
||||
texts.iter().any(|t| t.contains("20 row(s) seeded into users")),
|
||||
texts
|
||||
.iter()
|
||||
.any(|t| t.contains("20 row(s) seeded into users")),
|
||||
"seeded-row count surfaced: {texts:?}",
|
||||
);
|
||||
assert!(
|
||||
texts.iter().any(|t| t.contains("status") && t.contains("generic text")),
|
||||
texts
|
||||
.iter()
|
||||
.any(|t| t.contains("status") && t.contains("generic text")),
|
||||
"the advisory names the enum-ish column: {texts:?}",
|
||||
);
|
||||
}
|
||||
@@ -7424,8 +7443,9 @@ mod tests {
|
||||
});
|
||||
let texts: Vec<String> = app.output.iter().map(|l| l.text.clone()).collect();
|
||||
assert!(
|
||||
texts.iter().any(|t| t.contains("4 row(s) seeded into J")
|
||||
&& t.contains("of 10 requested")),
|
||||
texts
|
||||
.iter()
|
||||
.any(|t| t.contains("4 row(s) seeded into J") && t.contains("of 10 requested")),
|
||||
"the cap note surfaces requested vs produced: {texts:?}",
|
||||
);
|
||||
}
|
||||
@@ -7464,7 +7484,9 @@ mod tests {
|
||||
});
|
||||
let texts: Vec<String> = app.output.iter().map(|l| l.text.clone()).collect();
|
||||
assert!(
|
||||
texts.iter().any(|t| t.contains("2 row(s) deleted in `Orders`")),
|
||||
texts
|
||||
.iter()
|
||||
.any(|t| t.contains("2 row(s) deleted in `Orders`")),
|
||||
"cascade summary still surfaces alongside RETURNING: {texts:?}",
|
||||
);
|
||||
assert!(
|
||||
|
||||
Reference in New Issue
Block a user