runShaderConverterCli function
Future<ShaderConverterCliResult>
runShaderConverterCli(
- List<
String> args, { - StringSink? out,
- StringSink? err,
Runs the shader conversion CLI with args.
Returns a structured summary and does not call exit() directly.
Implementation
Future<ShaderConverterCliResult> runShaderConverterCli(
List<String> args, {
StringSink? out,
StringSink? err,
}) async {
final stdoutSink = out ?? stdout;
final stderrSink = err ?? stderr;
_ParsedArgs parsed;
try {
parsed = _parseArgs(args);
} on FormatException catch (error) {
stderrSink.writeln(error.message);
_printUsage(stdoutSink);
return const ShaderConverterCliResult(
exitCode: 64,
convertedCount: 0,
skippedCount: 0,
failedCount: 0,
);
}
if (parsed.showHelp) {
_printUsage(stdoutSink);
return const ShaderConverterCliResult(
exitCode: 0,
convertedCount: 0,
skippedCount: 0,
failedCount: 0,
);
}
final inputEntity = FileSystemEntity.typeSync(parsed.inputPath);
if (inputEntity == FileSystemEntityType.notFound) {
stderrSink.writeln('Input path not found: ${parsed.inputPath}');
return const ShaderConverterCliResult(
exitCode: 2,
convertedCount: 0,
skippedCount: 0,
failedCount: 0,
);
}
final outputDir = Directory(parsed.outputDirPath);
if (!parsed.dryRun) {
outputDir.createSync(recursive: true);
}
Directory? propertiesDir;
if (parsed.propertiesDirPath != null) {
propertiesDir = Directory(parsed.propertiesDirPath!);
if (!parsed.dryRun) {
propertiesDir.createSync(recursive: true);
}
}
final sourceFiles = _collectSourceFiles(parsed.inputPath, inputEntity);
if (sourceFiles.isEmpty) {
stderrSink.writeln('No .glsl files found from input: ${parsed.inputPath}');
return const ShaderConverterCliResult(
exitCode: 2,
convertedCount: 0,
skippedCount: 0,
failedCount: 0,
);
}
final converter = ShaderConverter();
var convertedCount = 0;
var failedCount = 0;
var skippedCount = 0;
for (final sourceFile in sourceFiles) {
final sourceBaseName = _basenameWithoutExtension(sourceFile.path);
final outputBaseName = parsed.fileNameMap[sourceBaseName] ?? sourceBaseName;
final targetShaderPath = '${outputDir.path}/$outputBaseName.frag';
final targetShaderFile = File(targetShaderPath);
ShaderConversionOutput result;
try {
final source = sourceFile.readAsStringSync();
result = converter.convert(source);
} on IOException catch (error) {
failedCount++;
stderrSink.writeln('[FAIL] ${sourceFile.path} -> $targetShaderPath');
stderrSink.writeln(' - [error] $error');
continue;
}
if (result.hasErrors) {
failedCount++;
stderrSink.writeln('[FAIL] ${sourceFile.path} -> $targetShaderPath');
for (final issue in result.issues) {
final level = issue.isError ? 'error' : 'warn';
stderrSink.writeln(' - [$level] ${issue.message}');
}
continue;
}
if (!parsed.overwrite && targetShaderFile.existsSync()) {
skippedCount++;
stdoutSink.writeln(
'[SKIP] $targetShaderPath already exists (use --overwrite).');
continue;
}
if (!parsed.dryRun) {
final shaderOutput = parsed.mobileCompatible
? _makeMobileCompatibleShader(result.shaderSource)
: result.shaderSource;
targetShaderFile.writeAsStringSync(shaderOutput);
if (propertiesDir != null) {
final propertiesPath = '${propertiesDir.path}/$outputBaseName.json';
final propertiesFile = File(propertiesPath);
propertiesFile.writeAsStringSync(result.propertiesJson());
}
}
convertedCount++;
stdoutSink.writeln('[OK] ${sourceFile.path} -> $targetShaderPath');
for (final issue in result.issues) {
stdoutSink.writeln(' - [warn] ${issue.message}');
}
}
stdoutSink.writeln('');
stdoutSink.writeln('Conversion summary:');
stdoutSink.writeln(' Converted: $convertedCount');
stdoutSink.writeln(' Skipped: $skippedCount');
stdoutSink.writeln(' Failed: $failedCount');
return ShaderConverterCliResult(
exitCode: failedCount > 0 ? 1 : 0,
convertedCount: convertedCount,
skippedCount: skippedCount,
failedCount: failedCount,
);
}