add hanatui
This commit is contained in:
@@ -0,0 +1,313 @@
|
||||
using HanaTui.Hana;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace HanaTui.Tui;
|
||||
|
||||
/// <summary>
|
||||
/// Guided input forms for each operation.
|
||||
/// Each method collects all needed parameters, then returns a typed params object.
|
||||
/// Returns null if the user cancelled.
|
||||
/// </summary>
|
||||
public static class OperationForms
|
||||
{
|
||||
// -------------------------------------------------------------------------
|
||||
// Shared helpers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Fetches the schema list with a spinner, then shows an arrow-key picker.
|
||||
/// Returns the selected schema, or null if cancelled.
|
||||
/// </summary>
|
||||
private static string? PickSchema(string userKey, string title)
|
||||
{
|
||||
List<string> schemas = [];
|
||||
string? error = null;
|
||||
|
||||
AnsiConsole.Status()
|
||||
.Spinner(Spinner.Known.Dots)
|
||||
.SpinnerStyle(Style.Parse("blue"))
|
||||
.Start("[blue]Fetching schemas...[/]", _ =>
|
||||
{
|
||||
var (success, list, err) = HdbCliRunner.ListSchemas(userKey);
|
||||
if (success)
|
||||
schemas = list;
|
||||
else
|
||||
error = err;
|
||||
});
|
||||
|
||||
if (error is not null)
|
||||
AnsiConsole.MarkupLine($"[yellow][[WARN]] Could not fetch schemas: {Markup.Escape(error)}[/]");
|
||||
|
||||
var choices = new List<string>(schemas);
|
||||
choices.Add("[dim][ Enter manually ][/]");
|
||||
choices.Add("[dim][ Cancel ][/]");
|
||||
|
||||
var prompt = new SelectionPrompt<string>()
|
||||
.Title($"[bold]{title}[/]")
|
||||
.PageSize(15)
|
||||
.HighlightStyle(Style.Parse("bold dodgerblue1"))
|
||||
.AddChoices(choices);
|
||||
|
||||
var selected = AnsiConsole.Prompt(prompt);
|
||||
|
||||
if (selected.Contains("Cancel"))
|
||||
return null;
|
||||
|
||||
if (selected.Contains("manually"))
|
||||
{
|
||||
var manual = AnsiConsole.Ask<string>("Enter schema name:").Trim();
|
||||
return string.IsNullOrWhiteSpace(manual) ? null : manual;
|
||||
}
|
||||
|
||||
return selected.Trim();
|
||||
}
|
||||
|
||||
private static int PickThreads(string label = "Number of threads")
|
||||
{
|
||||
var maxThreads = Environment.ProcessorCount;
|
||||
var defaultThreads = Math.Max(1, maxThreads / 2);
|
||||
return AnsiConsole.Ask($"{label} [dim](default={defaultThreads}, max={maxThreads})[/]:",
|
||||
defaultThreads);
|
||||
}
|
||||
|
||||
private static bool ConfirmYesNo(string question, bool defaultYes = false)
|
||||
{
|
||||
var defaultLabel = defaultYes ? "Y/n" : "y/N";
|
||||
var answer = AnsiConsole.Ask<string>($"{question} [dim]({defaultLabel})[/]:", defaultYes ? "y" : "n");
|
||||
return answer.Trim().Equals("y", StringComparison.OrdinalIgnoreCase) ||
|
||||
answer.Trim().Equals("yes", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static void PrintOperationHeader(string title)
|
||||
{
|
||||
AnsiConsole.Clear();
|
||||
var rule = new Rule($"[bold dodgerblue1]{title}[/]").RuleStyle(Style.Parse("dodgerblue1"));
|
||||
AnsiConsole.Write(rule);
|
||||
AnsiConsole.WriteLine();
|
||||
}
|
||||
|
||||
private static bool ShowSummaryAndConfirm(string title, Dictionary<string, string> fields)
|
||||
{
|
||||
AnsiConsole.WriteLine();
|
||||
var table = new Table().BorderColor(Color.DodgerBlue1).Border(TableBorder.Rounded);
|
||||
table.AddColumn("[bold]Parameter[/]");
|
||||
table.AddColumn("[bold]Value[/]");
|
||||
foreach (var (k, v) in fields)
|
||||
table.AddRow($"[dim]{k}[/]", $"[yellow]{Markup.Escape(v)}[/]");
|
||||
AnsiConsole.MarkupLine($"[bold]{title}[/]");
|
||||
AnsiConsole.Write(table);
|
||||
AnsiConsole.WriteLine();
|
||||
|
||||
return ConfirmYesNo("Proceed with this operation?", defaultYes: true);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Export
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public static ExportParams? ExportForm(string userKey)
|
||||
{
|
||||
PrintOperationHeader("Export Schema");
|
||||
|
||||
var schema = PickSchema(userKey, "Select schema to export:");
|
||||
if (schema is null) return null;
|
||||
|
||||
var targetPath = AnsiConsole.Ask<string>("Target directory or file path [dim](.tar.gz for archive)[/]:").Trim();
|
||||
if (string.IsNullOrWhiteSpace(targetPath)) return null;
|
||||
|
||||
var threads = PickThreads();
|
||||
var compress = ConfirmYesNo("Compress output as .tar.gz?");
|
||||
|
||||
var confirmed = ShowSummaryAndConfirm("Export Summary", new Dictionary<string, string>
|
||||
{
|
||||
["Schema"] = schema,
|
||||
["Target path"] = targetPath,
|
||||
["Threads"] = threads.ToString(),
|
||||
["Compress"] = compress ? "Yes" : "No",
|
||||
});
|
||||
|
||||
if (!confirmed) return null;
|
||||
|
||||
return new ExportParams
|
||||
{
|
||||
Schema = schema,
|
||||
TargetPath = targetPath,
|
||||
Threads = threads,
|
||||
Compress = compress,
|
||||
};
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Import
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public static ImportParams? ImportForm(string userKey, bool renameMode)
|
||||
{
|
||||
var title = renameMode ? "Import & Rename Schema" : "Import Schema";
|
||||
PrintOperationHeader(title);
|
||||
|
||||
var sourceSchema = AnsiConsole.Ask<string>("Source schema name [dim](as it was exported)[/]:").Trim();
|
||||
if (string.IsNullOrWhiteSpace(sourceSchema)) return null;
|
||||
|
||||
string? newSchemaName = null;
|
||||
if (renameMode)
|
||||
{
|
||||
newSchemaName = AnsiConsole.Ask<string>("New target schema name:").Trim();
|
||||
if (string.IsNullOrWhiteSpace(newSchemaName)) return null;
|
||||
}
|
||||
|
||||
var sourcePath = AnsiConsole.Ask<string>("Source path [dim](directory or .tar.gz)[/]:").Trim();
|
||||
if (string.IsNullOrWhiteSpace(sourcePath)) return null;
|
||||
|
||||
var threads = PickThreads();
|
||||
var replace = ConfirmYesNo("Replace existing objects?");
|
||||
|
||||
var summary = new Dictionary<string, string>
|
||||
{
|
||||
["Source schema"] = sourceSchema,
|
||||
["Source path"] = sourcePath,
|
||||
["Threads"] = threads.ToString(),
|
||||
["Replace"] = replace ? "Yes" : "No (IGNORE EXISTING)",
|
||||
};
|
||||
if (renameMode && newSchemaName is not null)
|
||||
summary["New schema name"] = newSchemaName;
|
||||
|
||||
var confirmed = ShowSummaryAndConfirm($"{title} Summary", summary);
|
||||
if (!confirmed) return null;
|
||||
|
||||
return new ImportParams
|
||||
{
|
||||
SourceSchema = sourceSchema,
|
||||
NewSchemaName = newSchemaName,
|
||||
SourcePath = sourcePath,
|
||||
Threads = threads,
|
||||
Replace = replace,
|
||||
};
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Copy
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public static CopyParams? CopyForm(string userKey)
|
||||
{
|
||||
PrintOperationHeader("Copy Schema");
|
||||
|
||||
var sourceSchema = PickSchema(userKey, "Select source schema:");
|
||||
if (sourceSchema is null) return null;
|
||||
|
||||
var targetSchema = AnsiConsole.Ask<string>("Target schema name:").Trim();
|
||||
if (string.IsNullOrWhiteSpace(targetSchema)) return null;
|
||||
|
||||
var tempPath = AnsiConsole.Ask<string>("Temporary export directory path:").Trim();
|
||||
if (string.IsNullOrWhiteSpace(tempPath)) return null;
|
||||
|
||||
var threads = PickThreads();
|
||||
var replace = ConfirmYesNo("Replace existing objects in target schema?");
|
||||
|
||||
var confirmed = ShowSummaryAndConfirm("Copy Schema Summary", new Dictionary<string, string>
|
||||
{
|
||||
["Source schema"] = sourceSchema,
|
||||
["Target schema"] = targetSchema,
|
||||
["Temp path"] = tempPath,
|
||||
["Threads"] = threads.ToString(),
|
||||
["Replace"] = replace ? "Yes" : "No",
|
||||
});
|
||||
|
||||
if (!confirmed) return null;
|
||||
|
||||
return new CopyParams
|
||||
{
|
||||
SourceSchema = sourceSchema,
|
||||
TargetSchema = targetSchema,
|
||||
TempPath = tempPath,
|
||||
Threads = threads,
|
||||
Replace = replace,
|
||||
};
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Drop
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public static DropParams? DropForm(string userKey)
|
||||
{
|
||||
PrintOperationHeader("Drop Schema");
|
||||
|
||||
var schema = PickSchema(userKey, "Select schema to drop:");
|
||||
if (schema is null) return null;
|
||||
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.MarkupLine($"[bold red]WARNING:[/] You are about to [bold red]permanently drop[/] schema [bold yellow]{Markup.Escape(schema)}[/].");
|
||||
AnsiConsole.MarkupLine("[red]This cannot be undone.[/]\n");
|
||||
|
||||
var confirm = AnsiConsole.Ask<string>($"Type [bold red]YES[/] to confirm dropping [yellow]{Markup.Escape(schema)}[/]:").Trim();
|
||||
if (confirm != "YES")
|
||||
{
|
||||
AnsiConsole.MarkupLine("[yellow]Operation cancelled.[/]");
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.MarkupLine("[dim]Press any key to return...[/]");
|
||||
Console.ReadKey(intercept: true);
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DropParams { Schema = schema };
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Rename DB
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public static RenameDbParams? RenameDbForm(string userKey)
|
||||
{
|
||||
PrintOperationHeader("Rename Database (Company Name)");
|
||||
|
||||
var schema = PickSchema(userKey, "Select schema:");
|
||||
if (schema is null) return null;
|
||||
|
||||
var newName = AnsiConsole.Ask<string>("New company name:").Trim();
|
||||
if (string.IsNullOrWhiteSpace(newName)) return null;
|
||||
|
||||
var confirmed = ShowSummaryAndConfirm("Rename DB Summary", new Dictionary<string, string>
|
||||
{
|
||||
["Schema"] = schema,
|
||||
["New company name"] = newName,
|
||||
["Tables updated"] = "CINF, OADM",
|
||||
});
|
||||
|
||||
if (!confirmed) return null;
|
||||
|
||||
return new RenameDbParams { Schema = schema, NewCompanyName = newName };
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Backup
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public static BackupParams? BackupForm(string userKey)
|
||||
{
|
||||
PrintOperationHeader("Backup Tenant");
|
||||
|
||||
var targetPath = AnsiConsole.Ask<string>("Target directory path:").Trim();
|
||||
if (string.IsNullOrWhiteSpace(targetPath)) return null;
|
||||
|
||||
var threads = PickThreads("Number of compression threads");
|
||||
var compress = ConfirmYesNo("Compress backup as .tar.gz?");
|
||||
|
||||
var confirmed = ShowSummaryAndConfirm("Backup Summary", new Dictionary<string, string>
|
||||
{
|
||||
["Target path"] = targetPath,
|
||||
["Threads"] = threads.ToString(),
|
||||
["Compress"] = compress ? "Yes" : "No",
|
||||
});
|
||||
|
||||
if (!confirmed) return null;
|
||||
|
||||
return new BackupParams
|
||||
{
|
||||
TargetPath = targetPath,
|
||||
Threads = threads,
|
||||
Compress = compress,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user