using GitConverter.Lib.Converters;
using GitConverter.Lib.Factories;
using GitConverter.Lib.Licensing;
using GitConverter.Lib.Logging;
using GitConverter.Lib.Models;
namespace GitConverter.ConsoleApp
{
///
/// Entry point and CLI dispatch for the GitConverter console application.
///
///
/// - Exposes a single command: gis_convert, which converts an input GIS file/archive to a target format.
/// - This class is intentionally small: parsing, validation and host-level concerns are handled here;
/// conversion logic is delegated to and implementations.
/// - The CLI returns numeric exit codes to callers (0=success, 1=app error, 2=conversion failed, 3=unexpected).
/// - Logging is configured on demand via the optional Log <path> [level] arguments and wired into the
/// library using / .
/// - Keep changes to this file minimal and deterministic because unit/integration tests rely on its behavior.
///
public class Program
{
// Exit codes for the CLI
private enum ExitCode : int
{
Success = 0, // Successful execution
AppError = 1, // invalid args, wrong command
ConversionFailed = 2, // converter returned Failure or null
Unexpected = 3, // unexpected exception
}
///
/// Process entry point. Sets up basic host concerns and delegates to .
///
/// Command-line arguments supplied by the caller.
///
/// - Sets to UTF-8 to support emoji and international characters in console output.
/// - Uses to optionally provide developer-friendly defaults in DEBUG builds only.
/// - Calls and exits the process with the returned exit code.
/// - Keep this method minimal and non-throwing; all recoverable errors are reported via exit codes and console output.
///
private static void Main(string[] args)
{
// Apply license once at startup
if (!AsposeLicenseManager.ApplyLicense())
{
Console.Error.WriteLine("⚠️ Failed to apply Aspose license. Conversions may fail or produce watermarked output.");
Environment.Exit((int)ExitCode.AppError);
}
// Enable Unicode (UTF-8) output for emoji and international characters
Console.OutputEncoding = System.Text.Encoding.UTF8;
// Defensive debug helper: only returns defaults when running under DEBUG and args are missing.
args = EnsureDebugArgs(args);
Environment.Exit(Run(args));
}
///
/// Returns debug default arguments when running under a debugger and the caller supplied insufficient args.
///
/// Original CLI arguments. May be null.
///
/// The original when populated; otherwise a curated set of debug arguments selected via
/// the environment variable GITCONVERTER_DEBUG_TARGET. In non-DEBUG builds the original args are returned.
///
///
/// - This method is compiled only in DEBUG builds (guarded by #if DEBUG); it has no effect in Release/CI.
/// - Use the environment variable GITCONVERTER_DEBUG_TARGET to pick a scenario (examples: Shapefile1, Csv1, Gml1).
/// - Keep debug defaults out of production binaries to avoid surprising behavior in CI/consumer environments.
///
private static string[] EnsureDebugArgs(string[] args)
{
#if DEBUG
if (args != null && args.Length >= 6)
return args;
// Allow selecting a debug scenario via environment variable.
// Supported values: Shapefile (default), Csv, Gml.
var debugTarget = Environment.GetEnvironmentVariable("GITCONVERTER_DEBUG_TARGET");
switch (debugTarget.Trim().ToLowerInvariant())
{
// Shapefile test cases
case "shapefile1":
return new[]
{
"gis_convert",
@"D:\GisConverter\Tests\Shapefile\Input\ShapeFiles.7z",
"GeoJson",
@"D:\GisConverter\Tests\Shapefile\Output\Shapefile1",
@"D:\GisConverter\Tests\Shapefile\Temp\Shapefile1",
"Log",
@"D:\GisConverter\Tests\Shapefile\Log\shapefile_log1.txt"
};
case "shapefile2":
return new[]
{
"gis_convert",
@"D:\GisConverter\Tests\Shapefile\Input\Vector.7z",
"Shapefile",
@"D:\GisConverter\Tests\Shapefile\Output\Shapefile2",
@"D:\GisConverter\Tests\Shapefile\Temp\Shapefile2",
"Log",
@"D:\GisConverter\Tests\Shapefile\Log\shapefile_log2.txt"
};
case "shapefile3":
return new[]
{
"gis_convert",
@"D:\GisConverter\Tests\Shapefile\Input\לא סטטוטורי - גבול מחוז מאוחד.7z",
"Shapefile",
@"D:\GisConverter\Tests\Shapefile\Output\Shapefile3",
@"D:\GisConverter\Tests\Shapefile\Temp\Shapefile3",
"Log",
@"D:\GisConverter\Tests\Shapefile\Log\shapefile_log3.txt"
};
case "shapefile4":
return new[]
{
"gis_convert",
@"D:\GisConverter\Tests\Shapefile\Input\שכבות מידע (Arc View).7z",
"Shapefile",
@"D:\GisConverter\Tests\Shapefile\Output\Shapefile4",
@"D:\GisConverter\Tests\Shapefile\Temp\Shapefile4",
"Log",
@"D:\GisConverter\Tests\Shapefile\Log\shapefile_log4.txt"
};
// Csv test cases
case "csv1":
return new[]
{
"gis_convert",
@"D:\GisConverter\Tests\Csv\Input\features.7z",
"Csv",
@"D:\GisConverter\Tests\Csv\Output\Csv1",
@"D:\GisConverter\Tests\Csv\Temp\Csv1",
"Log",
@"D:\GisConverter\Tests\Csv\Log\csv_log1.txt"
};
case "csv2":
return new[]
{
"gis_convert",
@"D:\GisConverter\Tests\Csv\Input\features.csv",
"Csv",
@"D:\GisConverter\Tests\Csv\Output\Csv2",
@"D:\GisConverter\Tests\Csv\Temp\Csv2",
"Log",
@"D:\GisConverter\Tests\Csv\Log\csv_log2.txt"
};
case "csv3":
return new[]
{
"gis_convert",
@"D:\GisConverter\Tests\Csv\Input\features.zip",
"Csv",
@"D:\GisConverter\Tests\Csv\Output\Csv3",
@"D:\GisConverter\Tests\Csv\Temp\Csv3",
"Log",
@"D:\GisConverter\Tests\Csv\Log\csv_log3.txt"
};
// Gml test cases
case "gml1":
return new[]
{
"gis_convert",
@"D:\GisConverter\Tests\Gml\Input\gml_with_attribute_collection_schema.7z",
"Gml",
@"D:\GisConverter\Tests\Gml\Output\Gml1",
@"D:\GisConverter\Tests\Gml\Temp\Gml1",
"Log",
@"D:\GisConverter\Tests\Gml\Log\gml_log1.txt"
};
case "gml2":
return new[]
{
"gis_convert",
@"D:\GisConverter\Tests\Gml\Input\gml_with_attribute_collection_schema.zip",
"Gml",
@"D:\GisConverter\Tests\Gml\Output\Gml2",
@"D:\GisConverter\Tests\Gml\Temp\Gml2",
"Log",
@"D:\GisConverter\Tests\Gml\Log\gml_log2.txt"
};
case "gml3":
return new[]
{
"gis_convert",
@"D:\GisConverter\Tests\Gml\Input\gml_without_attribute_collection_schema.7z",
"Gml",
@"D:\GisConverter\Tests\Gml\Output\Gml3",
@"D:\GisConverter\Tests\Gml\Temp\Gml3",
"Log",
@"D:\GisConverter\Tests\Gml\Log\gml_log3.txt"
};
case "gml4":
return new[]
{
"gis_convert",
@"D:\GisConverter\Tests\Gml\Input\gml_without_attribute_collection_schema.gml",
"Gml",
@"D:\GisConverter\Tests\Gml\Output\Gml4",
@"D:\GisConverter\Tests\Gml\Temp\Gml4",
"Log",
@"D:\GisConverter\Tests\Gml\Log\gml_log4.txt"
};
case "gml5":
return new[]
{
"gis_convert",
@"D:\GisConverter\Tests\Gml\Input\gml_without_attribute_collection_schema.zip",
"Gml",
@"D:\GisConverter\Tests\Gml\Output\Gml5",
@"D:\GisConverter\Tests\Gml\Temp\Gml5",
"Log",
@"D:\GisConverter\Tests\Gml\Log\gml_log5.txt"
};
default:
return args ?? Array.Empty();
}
#else
return args ?? Array.Empty();
#endif
}
///
/// Main dispatcher for command-line operations.
///
/// Command-line arguments passed to the application.
/// Integer exit code describing result (see ).
///
/// - Valid top-level commands:
/// - gis_convert — convert GIS input to a target format.
/// - Recognizes help flags: help, --help, -h and --help-formats.
/// - Returns when help is displayed so CI/help automation reports success.
/// - All unexpected exceptions are caught and result in ; the exception
/// message is printed to console for diagnostics.
/// - The method intentionally avoids throwing; callers (test harnesses, CI) inspect the returned code.
///
public static int Run(string[] args)
{
if (args.Length == 0 || args[0] == "help" || args[0] == "--help" || args[0] == "-h")
{
ShowHelp();
return (int)ExitCode.Success;
}
if (args[0] == "--help-formats")
{
ShowHelpFormats();
return (int)ExitCode.Success;
}
// Extract the command from the first argument.
string command = args[0].ToLowerInvariant();
try
{
switch (command)
{
case "gis_convert":
return RunGisConverter(args);
default:
Console.WriteLine($"❌ unknown command: {command}\n");
ShowHelp();
return (int)ExitCode.AppError;
}
}
catch (Exception ex)
{
Console.WriteLine("❌ error:");
Console.WriteLine(ex.Message);
return (int)ExitCode.Unexpected;
}
}
///
/// Executes the gis_convert command and returns an exit code.
///
/// Command-line arguments where:
/// args[1] = input path, args[2] = target format, args[3] = output folder, args[4] = temp folder,
/// optional args starting at index 5 may contain logging options.
/// Exit code representing the command outcome.
///
/// - Validates argument count and prints usage when insufficient arguments are provided.
/// - Attempts to repair unquoted input paths that contain spaces via .
/// - Parses optional logging arguments using and initializes logging
/// via (best-effort; failures do not abort conversion).
/// - Detects an appropriate converter via and invokes conversion.
/// - Conversion results:
/// - If is null or indicates failure the method prints diagnostics and returns
/// .
/// - On success prints timestamps and messages and returns .
/// - All converter exceptions are caught and reported; the method returns .
/// - Keep user-facing messages concise and localized to the console; diagnostics for debugging should be written to logs.
///
private static int RunGisConverter(string[] args)
{
// Require at least: command (gis_convert), gis input file path, gis target format option, output folder path, out temp folder.
if (args.Length < 6)
{
Console.WriteLine("usage: gis_convert