fix core dump on export option
This commit is contained in:
Binary file not shown.
@@ -10,9 +10,16 @@ namespace HanaTui.Tui;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class KeySelectionScreen
|
public static class KeySelectionScreen
|
||||||
{
|
{
|
||||||
// Sentinel values for the non-key choices
|
private sealed class KeyChoice
|
||||||
private const string ManualEntry = "__MANUAL__";
|
{
|
||||||
private const string ExitChoice = "__EXIT__";
|
public string Display { get; init; } = "";
|
||||||
|
public string? KeyName { get; init; } // null = exit, "" = manual entry
|
||||||
|
|
||||||
|
public static readonly KeyChoice Manual = new() { Display = "[ Enter key name manually ]", KeyName = "" };
|
||||||
|
public static readonly KeyChoice Exit = new() { Display = "[ Exit ]", KeyName = null };
|
||||||
|
|
||||||
|
public override string ToString() => Display;
|
||||||
|
}
|
||||||
|
|
||||||
public static string? Run()
|
public static string? Run()
|
||||||
{
|
{
|
||||||
@@ -30,14 +37,11 @@ public static class KeySelectionScreen
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show client path for reference
|
|
||||||
var clientDir = HdbClientLocator.ClientDirectory;
|
var clientDir = HdbClientLocator.ClientDirectory;
|
||||||
if (clientDir is not null)
|
if (clientDir is not null)
|
||||||
AnsiConsole.MarkupLine($"[dim]HDB client: {Markup.Escape(clientDir)}[/]\n");
|
AnsiConsole.MarkupLine($"[dim]HDB client: {Markup.Escape(clientDir)}[/]\n");
|
||||||
|
|
||||||
// Load keys with a spinner
|
|
||||||
List<HdbUserstoreKey> keys = [];
|
List<HdbUserstoreKey> keys = [];
|
||||||
|
|
||||||
AnsiConsole.Status()
|
AnsiConsole.Status()
|
||||||
.Spinner(Spinner.Known.Dots)
|
.Spinner(Spinner.Known.Dots)
|
||||||
.SpinnerStyle(Style.Parse("blue"))
|
.SpinnerStyle(Style.Parse("blue"))
|
||||||
@@ -52,52 +56,34 @@ public static class KeySelectionScreen
|
|||||||
AnsiConsole.MarkupLine("You can still enter a key name manually.\n");
|
AnsiConsole.MarkupLine("You can still enter a key name manually.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build a plain-string list where the value IS the key name (or sentinel).
|
// Build typed choices. Display is plain text — never parsed as markup.
|
||||||
// We use SelectionPrompt<string> with a display converter so Spectre renders
|
var choices = keys
|
||||||
// the label as markup but we always get back the plain key name.
|
.Select(k => new KeyChoice { Display = BuildKeyDisplay(k), KeyName = k.Name })
|
||||||
var choiceNames = new List<string>();
|
.ToList();
|
||||||
var displayMap = new Dictionary<string, string>(); // name -> display label
|
choices.Add(KeyChoice.Manual);
|
||||||
|
choices.Add(KeyChoice.Exit);
|
||||||
|
|
||||||
foreach (var k in keys)
|
var prompt = new SelectionPrompt<KeyChoice>()
|
||||||
{
|
|
||||||
choiceNames.Add(k.Name);
|
|
||||||
displayMap[k.Name] = BuildKeyDisplay(k);
|
|
||||||
}
|
|
||||||
|
|
||||||
choiceNames.Add(ManualEntry);
|
|
||||||
displayMap[ManualEntry] = "[ Enter key name manually ]";
|
|
||||||
|
|
||||||
choiceNames.Add(ExitChoice);
|
|
||||||
displayMap[ExitChoice] = "[ Exit ]";
|
|
||||||
|
|
||||||
var prompt = new SelectionPrompt<string>()
|
|
||||||
.Title("[bold]Select HDBUSERSTORE key:[/]")
|
.Title("[bold]Select HDBUSERSTORE key:[/]")
|
||||||
.PageSize(15)
|
.PageSize(15)
|
||||||
.HighlightStyle(Style.Parse("bold dodgerblue1"))
|
.HighlightStyle(Style.Parse("bold dodgerblue1"))
|
||||||
.UseConverter(name => displayMap.TryGetValue(name, out var label)
|
.UseConverter(c => Markup.Escape(c.Display))
|
||||||
? Markup.Escape(label)
|
.AddChoices(choices);
|
||||||
: Markup.Escape(name))
|
|
||||||
.AddChoices(choiceNames);
|
|
||||||
|
|
||||||
var selected = AnsiConsole.Prompt(prompt);
|
var selected = AnsiConsole.Prompt(prompt);
|
||||||
|
|
||||||
if (selected == ExitChoice)
|
if (selected.KeyName is null)
|
||||||
return null;
|
return null; // Exit
|
||||||
|
|
||||||
if (selected == ManualEntry)
|
if (selected.KeyName == "")
|
||||||
{
|
{
|
||||||
var manual = AnsiConsole.Ask<string>("[bold]Enter HDBUSERSTORE key name:[/]").Trim();
|
var manual = AnsiConsole.Ask<string>("[bold]Enter HDBUSERSTORE key name:[/]").Trim();
|
||||||
return string.IsNullOrWhiteSpace(manual) ? null : manual;
|
return string.IsNullOrWhiteSpace(manual) ? null : manual;
|
||||||
}
|
}
|
||||||
|
|
||||||
// selected is already the plain key name
|
return selected.KeyName;
|
||||||
return selected;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Builds a plain-text display label (no markup) for a key.
|
|
||||||
/// Spectre's UseConverter renders the returned string as plain text.
|
|
||||||
/// </summary>
|
|
||||||
private static string BuildKeyDisplay(HdbUserstoreKey k)
|
private static string BuildKeyDisplay(HdbUserstoreKey k)
|
||||||
{
|
{
|
||||||
var sb = new SysText.StringBuilder(k.Name);
|
var sb = new SysText.StringBuilder(k.Name);
|
||||||
@@ -111,12 +97,11 @@ public static class KeySelectionScreen
|
|||||||
sb.Append(':');
|
sb.Append(':');
|
||||||
sb.Append(k.Port);
|
sb.Append(k.Port);
|
||||||
}
|
}
|
||||||
}
|
if (!string.IsNullOrEmpty(k.Tenant))
|
||||||
|
{
|
||||||
if (!string.IsNullOrEmpty(k.Tenant))
|
sb.Append('@');
|
||||||
{
|
sb.Append(k.Tenant);
|
||||||
sb.Append('@');
|
}
|
||||||
sb.Append(k.Tenant);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(k.User))
|
if (!string.IsNullOrEmpty(k.User))
|
||||||
|
|||||||
@@ -14,6 +14,21 @@ public static class OperationForms
|
|||||||
// Shared helpers
|
// Shared helpers
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A choice item that keeps the real value separate from the display label.
|
||||||
|
/// The display label is pre-escaped plain text — never interpreted as markup.
|
||||||
|
/// </summary>
|
||||||
|
private sealed class SchemaChoice
|
||||||
|
{
|
||||||
|
public string Display { get; init; } = "";
|
||||||
|
public string? Value { get; init; } // null = cancel, "" = manual entry
|
||||||
|
|
||||||
|
public static readonly SchemaChoice Manual = new() { Display = "[ Enter manually ]", Value = "" };
|
||||||
|
public static readonly SchemaChoice Cancel = new() { Display = "[ Cancel ]", Value = null };
|
||||||
|
|
||||||
|
public override string ToString() => Display;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fetches the schema list with a spinner, then shows an arrow-key picker.
|
/// Fetches the schema list with a spinner, then shows an arrow-key picker.
|
||||||
/// Returns the selected schema, or null if cancelled.
|
/// Returns the selected schema, or null if cancelled.
|
||||||
@@ -38,36 +53,35 @@ public static class OperationForms
|
|||||||
if (error is not null)
|
if (error is not null)
|
||||||
AnsiConsole.MarkupLine($"[yellow][[WARN]] Could not fetch schemas: {Markup.Escape(error)}[/]");
|
AnsiConsole.MarkupLine($"[yellow][[WARN]] Could not fetch schemas: {Markup.Escape(error)}[/]");
|
||||||
|
|
||||||
// Use sentinel strings as values so no markup leaks into choice labels
|
// Build typed choices. Display is plain text; ToString() is what SelectionPrompt shows.
|
||||||
const string manualSentinel = "__MANUAL__";
|
var choices = schemas
|
||||||
const string cancelSentinel = "__CANCEL__";
|
.Select(s => new SchemaChoice { Display = s, Value = s })
|
||||||
|
.ToList();
|
||||||
|
choices.Add(SchemaChoice.Manual);
|
||||||
|
choices.Add(SchemaChoice.Cancel);
|
||||||
|
|
||||||
var choiceValues = new List<string>(schemas) { manualSentinel, cancelSentinel };
|
// UseConverter returns Display which is already plain text.
|
||||||
|
// We also set the prompt to NOT interpret converter output as markup by
|
||||||
var prompt = new SelectionPrompt<string>()
|
// escaping it — belt-and-suspenders.
|
||||||
|
var prompt = new SelectionPrompt<SchemaChoice>()
|
||||||
.Title($"[bold]{Markup.Escape(title)}[/]")
|
.Title($"[bold]{Markup.Escape(title)}[/]")
|
||||||
.PageSize(15)
|
.PageSize(15)
|
||||||
.HighlightStyle(Style.Parse("bold dodgerblue1"))
|
.HighlightStyle(Style.Parse("bold dodgerblue1"))
|
||||||
.UseConverter(v => v switch
|
.UseConverter(c => Markup.Escape(c.Display))
|
||||||
{
|
.AddChoices(choices);
|
||||||
manualSentinel => "[ Enter manually ]",
|
|
||||||
cancelSentinel => "[ Cancel ]",
|
|
||||||
_ => Markup.Escape(v),
|
|
||||||
})
|
|
||||||
.AddChoices(choiceValues);
|
|
||||||
|
|
||||||
var selected = AnsiConsole.Prompt(prompt);
|
var selected = AnsiConsole.Prompt(prompt);
|
||||||
|
|
||||||
if (selected == cancelSentinel)
|
if (selected.Value is null)
|
||||||
return null;
|
return null; // Cancel
|
||||||
|
|
||||||
if (selected == manualSentinel)
|
if (selected.Value == "")
|
||||||
{
|
{
|
||||||
var manual = AnsiConsole.Ask<string>("Enter schema name:").Trim();
|
var manual = AnsiConsole.Ask<string>("Enter schema name:").Trim();
|
||||||
return string.IsNullOrWhiteSpace(manual) ? null : manual;
|
return string.IsNullOrWhiteSpace(manual) ? null : manual;
|
||||||
}
|
}
|
||||||
|
|
||||||
return selected.Trim();
|
return selected.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int PickThreads(string label = "Number of threads")
|
private static int PickThreads(string label = "Number of threads")
|
||||||
|
|||||||
Reference in New Issue
Block a user