Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Bond.Parser.CLI/Bond.Parser.CLI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,16 @@
<PackAsTool>true</PackAsTool>
<ToolCommandName>bbc</ToolCommandName>
<PackageId>bbc</PackageId>
<Version>0.2.2</Version>
<Authors>Mirco De Zorzi</Authors>
<Description>Bond IDL compiler and toolchain</Description>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<RootNamespace>Bond.Parser.CLI</RootNamespace>
</PropertyGroup>

<ItemGroup>
<None Include="../README.md" Pack="true" PackagePath="/" />
<None Include="../LICENSE" Pack="true" PackagePath="/" />
</ItemGroup>

</Project>
67 changes: 67 additions & 0 deletions Bond.Parser.CLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.IO;
using System.Linq;
using Bond.Parser.Parser;
using Bond.Parser.Formatting;
using Bond.Parser.Compatibility;
using Bond.Parser.Json;
using System.Text.Json;
Expand All @@ -26,6 +27,8 @@ static async Task<int> Main(string[] args)
{
"breaking" => await RunBreakingCommand(args[1..]),
"parse" => await RunParseCommand(args[1..]),
"fmt" => await RunFormatCommand(args[1..]),
"format" => await RunFormatCommand(args[1..]),
_ => await RunParseCommand(args) // Default to parse for backward compatibility
};
}
Expand All @@ -41,6 +44,7 @@ static void ShowHelp()
Console.WriteLine("Commands:");
Console.WriteLine(" parse Parse and validate a Bond schema file");
Console.WriteLine(" breaking Check for breaking changes against a reference schema");
Console.WriteLine(" format Format a Bond schema file");
Console.WriteLine();
Console.WriteLine("Parse Options:");
Console.WriteLine(" -v, --verbose Show detailed AST output");
Expand All @@ -52,10 +56,14 @@ static void ShowHelp()
Console.WriteLine(" --error-format <format> Output format: text, json (default: text)");
Console.WriteLine(" --ignore-imports Compare without resolving imports or types");
Console.WriteLine();
Console.WriteLine("Format Options:");
Console.WriteLine(" --check Exit non-zero if formatting is needed");
Console.WriteLine();
Console.WriteLine("Examples:");
Console.WriteLine(" bbc parse schema.bond");
Console.WriteLine(" bbc breaking schema.bond --against schema_v1.bond");
Console.WriteLine(" bbc breaking schema.bond --against .git#branch=main --error-format=json");
Console.WriteLine(" bbc format schema.bond");
Console.WriteLine();
Console.WriteLine("Global Options:");
Console.WriteLine(" -h, --help Show this help message");
Expand Down Expand Up @@ -154,6 +162,65 @@ static async Task<int> RunBreakingCommand(string[] args)
return await CheckBreaking(reference, filePath, errorFormat, verbose, ignoreImports);
}

static async Task<int> RunFormatCommand(string[] args)
{
if (args.Length == 0)
{
WriteError("Error: No file specified");
ShowHelp();
return 1;
}

var filePath = args[0];
var check = args.Contains("--check");

if (!File.Exists(filePath))
{
WriteError($"Error: File not found: {filePath}");
return 1;
}

var content = await File.ReadAllTextAsync(filePath);
var result = BondFormatter.Format(content, Path.GetFullPath(filePath));

if (!result.Success)
{
Console.Error.WriteLine($"format failed: {filePath}");
foreach (var error in result.Errors)
{
Console.Error.WriteLine($"{error.Line}:{error.Column}: {error.Message}");
if (error.FilePath != null)
{
Console.Error.WriteLine($" in {error.FilePath}");
}
}
return 1;
}

if (result.FormattedText == null)
{
WriteError("Error: Format produced no output");
return 1;
}

if (check)
{
if (!string.Equals(content, result.FormattedText, StringComparison.Ordinal))
{
Console.Error.WriteLine($"{filePath} would be reformatted");
return 1;
}
return 0;
}

if (!string.Equals(content, result.FormattedText, StringComparison.Ordinal))
{
await File.WriteAllTextAsync(filePath, result.FormattedText);
}

return 0;
}

private sealed record ResolvedReference(
string FilePath,
string? Content,
Expand Down
79 changes: 79 additions & 0 deletions Bond.Parser.Tests/FormatterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using Bond.Parser.Formatting;
using FluentAssertions;

namespace Bond.Parser.Tests;

public class FormatterTests
{
[Fact]
public void Format_ReindentsAndSpaces()
{
var input = "namespace Test struct User{0:required string id;1:optional list<string> tags;}";
var expected = """
namespace Test

struct User {
0: required string id;
1: optional list<string> tags;
}
""";

var result = BondFormatter.Format(input, "<inline>");

result.Success.Should().BeTrue();
result.FormattedText.Should().Be(expected);
}

[Fact]
public void Format_PreservesComments()
{
var input = """
namespace Test
// user struct
struct User { /* fields */ 0: required string id; }
""";

var expected = """
namespace Test

// user struct
struct User {
/* fields */
0: required string id;
}
""";

var result = BondFormatter.Format(input, "<inline>");

result.Success.Should().BeTrue();
result.FormattedText.Should().Be(expected);
}

[Fact]
public void Format_TopLevelBlankLines()
{
var input = """
import "a.bond";import "b.bond";namespace Test struct A{0:required int32 id;} struct B{0:required int32 id;}
""";

var expected = """
import "a.bond";
import "b.bond";

namespace Test

struct A {
0: required int32 id;
}

struct B {
0: required int32 id;
}
""";

var result = BondFormatter.Format(input, "<inline>");

result.Success.Should().BeTrue();
result.FormattedText.Should().Be(expected);
}
}
21 changes: 12 additions & 9 deletions Bond.Parser/Bond.Parser.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@
<Nullable>enable</Nullable>
<IsPackable>true</IsPackable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Title>Bond IDL Parser</Title>
<PackageId>Bond.Parser</PackageId>
<NoWarn>CS1584,CS1658,CS1591</NoWarn>
<RootNamespace>Bond.Parser</RootNamespace>
</PropertyGroup>

<ItemGroup>
<None Include="../README.md" Pack="true" PackagePath="/" />
</ItemGroup>
<Title>Bond IDL Parser</Title>
<PackageId>Bond.Parser</PackageId>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<NoWarn>CS1584,CS1658,CS1591</NoWarn>
<RootNamespace>Bond.Parser</RootNamespace>
</PropertyGroup>

<ItemGroup>
<None Include="../README.md" Pack="true" PackagePath="/" />
<None Include="../LICENSE" Pack="true" PackagePath="/" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Antlr4.Runtime.Standard" Version="4.13.1" />
Expand Down
Loading
Loading