Optimised structs

This commit is contained in:
mdnapo 2024-09-16 20:12:48 +02:00
parent 23324a2b53
commit 74a141277e
47 changed files with 665 additions and 631 deletions

View File

@ -86,8 +86,8 @@ public class JsonParserTests
[ [
JsonNumber { Value: 1 }, JsonNumber { Value: 1 },
JsonBool { Value: true }, JsonBool { Value: true },
JsonObject { Properties: null}, JsonObject { Properties.Length: 0 },
JsonArray { Elements: null }, JsonArray { Elements.Length: 0 },
JsonNull JsonNull
] ]
}); });
@ -98,19 +98,19 @@ public class JsonParserTests
{ {
const string source = "{}"; const string source = "{}";
var tokens = JsonParser.Parse(nameof(Can_parse_an_empty_object), source); var tokens = JsonParser.Parse(nameof(Can_parse_an_empty_object), source);
Assert.That(tokens is JsonObject { Properties: null }); Assert.That(tokens is JsonObject { Properties.Length: 0 });
} }
[Test] [Test]
public void Can_parse_an_object_with_one_entry() public void Can_parse_an_object_with_one_entry()
{ {
const string source = "{\"first_name\":\"John\nDoe\"}"; const string source = "{\"first_name\":\"John\"}";
var node = JsonParser.Parse(nameof(Can_parse_an_object_with_one_entry), source); var node = JsonParser.Parse(nameof(Can_parse_an_object_with_one_entry), source);
Assert.That(node is JsonObject { Properties.Count: 1 }); Assert.That(node is JsonObject { Properties.Length: 1 });
var @object = (JsonObject)node; var @object = (JsonObject)node;
Assert.That(@object.Properties.ContainsKey("first_name")); Assert.That(@object.Properties.Any(property => property.Key == "first_name"));
Assert.That(@object.Properties["first_name"] is JsonString { Value: "John" }); Assert.That(@object.Properties.First(property => property.Key == "first_name").Value is JsonString { Value: "John" });
} }
[Test] [Test]
@ -121,12 +121,12 @@ public class JsonParserTests
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(node is JsonObject { Properties.Count: 2 }); Assert.That(node is JsonObject { Properties.Length: 2 });
var @object = (JsonObject)node; var @object = (JsonObject)node;
Assert.That(@object.Properties.ContainsKey("first_name")); Assert.That(@object.Properties.Any(property => property.Key == "first_name"));
Assert.That(@object.Properties["first_name"] is JsonString { Value: "John" }); Assert.That(@object.Properties.First(property => property.Key == "first_name").Value is JsonString { Value: "John" });
Assert.That(@object.Properties.ContainsKey("last_name")); Assert.That(@object.Properties.Any(property => property.Key == "last_name"));
Assert.That(@object.Properties["last_name"] is JsonString { Value: "Doe" }); Assert.That(@object.Properties.First(property => property.Key == "last_name").Value is JsonString { Value: "Doe" });
}); });
} }
} }

View File

@ -2,14 +2,14 @@ using DevDisciples.Parsing;
namespace DevDisciples.Json.Parser; namespace DevDisciples.Json.Parser;
public struct JsonArray : ISyntaxNode public readonly struct JsonArray : ISyntaxNode
{ {
public Lexer<JsonToken>.Token Token { get; } public Lexer<JsonToken>.Token Token { get; }
public List<ISyntaxNode> Elements { get; } public ISyntaxNode[] Elements { get; }
public JsonArray(Lexer<JsonToken>.Token token, List<ISyntaxNode>? elements) public JsonArray(Lexer<JsonToken>.Token token, ISyntaxNode[] elements)
{ {
Token = token; Token = token;
Elements = elements ?? new(); Elements = elements;
} }
} }

View File

@ -2,7 +2,7 @@ using DevDisciples.Parsing;
namespace DevDisciples.Json.Parser; namespace DevDisciples.Json.Parser;
public struct JsonBool : ISyntaxNode public readonly struct JsonBool : ISyntaxNode
{ {
public Lexer<JsonToken>.Token Token { get; } public Lexer<JsonToken>.Token Token { get; }
public bool Value => bool.TryParse(Token.Lexeme, out var val) && val; public bool Value => bool.TryParse(Token.Lexeme, out var val) && val;

View File

@ -2,7 +2,7 @@ using DevDisciples.Parsing;
namespace DevDisciples.Json.Parser; namespace DevDisciples.Json.Parser;
public struct JsonNull : ISyntaxNode public readonly struct JsonNull : ISyntaxNode
{ {
public Lexer<JsonToken>.Token Token { get; } public Lexer<JsonToken>.Token Token { get; }

View File

@ -2,7 +2,7 @@ using DevDisciples.Parsing;
namespace DevDisciples.Json.Parser; namespace DevDisciples.Json.Parser;
public struct JsonNumber : ISyntaxNode public readonly struct JsonNumber : ISyntaxNode
{ {
public Lexer<JsonToken>.Token Token { get; } public Lexer<JsonToken>.Token Token { get; }
public double Value => double.TryParse(Token.Lexeme.Replace('.', ','), out var val) ? val : default; public double Value => double.TryParse(Token.Lexeme.Replace('.', ','), out var val) ? val : default;

View File

@ -0,0 +1,18 @@
using DevDisciples.Parsing;
namespace DevDisciples.Json.Parser;
public readonly partial struct JsonObject
{
public readonly struct Property : ISyntaxNode
{
public string Key { get; }
public ISyntaxNode Value { get; }
public Property(string key, ISyntaxNode value)
{
Key = key;
Value = value;
}
}
}

View File

@ -2,14 +2,14 @@ using DevDisciples.Parsing;
namespace DevDisciples.Json.Parser; namespace DevDisciples.Json.Parser;
public struct JsonObject : ISyntaxNode public readonly partial struct JsonObject : ISyntaxNode
{ {
public Lexer<JsonToken>.Token Token { get; } public Lexer<JsonToken>.Token Token { get; }
public Dictionary<string, ISyntaxNode> Properties { get; set; } public Property[] Properties { get; }
public JsonObject(Lexer<JsonToken>.Token token, Dictionary<string, ISyntaxNode>? properties) public JsonObject(Lexer<JsonToken>.Token token, Property[] properties)
{ {
Token = token; Token = token;
Properties = properties ?? new(); Properties = properties;
} }
} }

View File

@ -51,7 +51,7 @@ public static partial class JsonParser
ctx.Consume(JsonToken.RightBracket, "Expected ']'"); ctx.Consume(JsonToken.RightBracket, "Expected ']'");
return new JsonArray(previous, elements); return new JsonArray(previous, elements?.ToArray() ?? System.Array.Empty<ISyntaxNode>());
} }
private static ISyntaxNode Object(ParserContext<JsonToken> ctx) private static ISyntaxNode Object(ParserContext<JsonToken> ctx)
@ -72,7 +72,9 @@ public static partial class JsonParser
ctx.Consume(JsonToken.RightBrace, "Expected '}'"); ctx.Consume(JsonToken.RightBrace, "Expected '}'");
return new JsonObject(previous, properties); var propertiesArray = properties?.Select(kv => new JsonObject.Property(kv.Key, kv.Value)).ToArray();
return new JsonObject(previous, propertiesArray ?? System.Array.Empty<JsonObject.Property>());
} }
private static ISyntaxNode Number(ParserContext<JsonToken> ctx) private static ISyntaxNode Number(ParserContext<JsonToken> ctx)

View File

@ -2,7 +2,7 @@ using DevDisciples.Parsing;
namespace DevDisciples.Json.Parser; namespace DevDisciples.Json.Parser;
public struct JsonString : ISyntaxNode public readonly struct JsonString : ISyntaxNode
{ {
public Lexer<JsonToken>.Token Token { get; } public Lexer<JsonToken>.Token Token { get; }
public string Value => Token.Lexeme; public string Value => Token.Lexeme;

View File

@ -1,43 +1,43 @@
using DevDisciples.Json.Tools.API.Requests; using DevDisciples.Json.Tools.API.Requests;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace DevDisciples.Json.Tools.API.Controllers; namespace DevDisciples.Json.Tools.API.Controllers;
[ApiController] [ApiController]
[Route("api/[controller]")] [Route("api/[controller]")]
public class TransformController : ControllerBase public class TransformController : ControllerBase
{ {
[HttpPost("uglify")] [HttpPost("uglify")]
public IActionResult Uglify([FromBody] UglifyRequest request) public IActionResult Uglify([FromBody] UglifyRequest request)
{ {
var context = new JsonFormatter.Context { Beautify = false }; var context = new JsonFormatter.Context { Beautify = false };
var result = JsonFormatter.Format(request.Source, context); var result = JsonFormatter.Format(request.Source, context);
return Ok(new { Result = result }); return Ok(new { Result = result });
} }
[HttpPost("prettify")] [HttpPost("prettify")]
public IActionResult Prettify([FromBody] PrettifyRequest request) public IActionResult Prettify([FromBody] PrettifyRequest request)
{ {
var context = new JsonFormatter.Context { Beautify = true, IndentSize = request.IndentSize }; var context = new JsonFormatter.Context { Beautify = true, IndentSize = request.IndentSize };
var result = JsonFormatter.Format(request.Source, context); var result = JsonFormatter.Format(request.Source, context);
return Ok(new { Result = result }); return Ok(new { Result = result });
} }
[HttpPost("json2csharp")] [HttpPost("json2csharp")]
public IActionResult Json2CSharp([FromBody] Json2CsharpRequest request) public IActionResult Json2CSharp([FromBody] Json2CsharpRequest request)
{ {
var context = new Json2CSharpTranslator.Context var context = new Json2CSharpTranslator.Context
{ {
RootClassName = request.RootClassName, RootClassName = request.RootClassName,
Namespace = request.Namespace, Namespace = request.Namespace,
}; };
var result = Json2CSharpTranslator.Translate(request.Source, context); var result = Json2CSharpTranslator.Translate(request.Source, context);
return Ok(new { Result = result }); return Ok(new { Result = result });
} }
} }

View File

@ -1,46 +1,46 @@
using DevDisciples.Parsing; using DevDisciples.Parsing;
using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace DevDisciples.Json.Tools.API; namespace DevDisciples.Json.Tools.API;
public class GlobalExceptionHandler : IExceptionHandler public class GlobalExceptionHandler : IExceptionHandler
{ {
public const int ParsingExceptionStatusCode = 499; public const int ParsingExceptionStatusCode = 499;
private readonly ILogger<GlobalExceptionHandler> _logger; private readonly ILogger<GlobalExceptionHandler> _logger;
public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger) public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
{ {
_logger = logger; _logger = logger;
} }
public async ValueTask<bool> TryHandleAsync( public async ValueTask<bool> TryHandleAsync(
HttpContext httpContext, HttpContext httpContext,
Exception exception, Exception exception,
CancellationToken cancellationToken CancellationToken cancellationToken
) )
{ {
switch (exception) switch (exception)
{ {
case ParsingException: case ParsingException:
_logger.LogError(exception, "An exception occurred: {Message}", exception.Message); _logger.LogError(exception, "An exception occurred: {Message}", exception.Message);
var problem = new ProblemDetails var problem = new ProblemDetails
{ {
Status = ParsingExceptionStatusCode, Status = ParsingExceptionStatusCode,
Title = "Parsing Exception", Title = "Parsing Exception",
Detail = exception.Message Detail = exception.Message
}; };
httpContext.Response.StatusCode = problem.Status.Value; httpContext.Response.StatusCode = problem.Status.Value;
await httpContext.Response.WriteAsJsonAsync(problem, cancellationToken); await httpContext.Response.WriteAsJsonAsync(problem, cancellationToken);
return true; return true;
default: default:
return false; return false;
} }
} }
} }

View File

@ -1,7 +1,7 @@
namespace DevDisciples.Json.Tools.API.Requests; namespace DevDisciples.Json.Tools.API.Requests;
public class Json2CsharpRequest : TransformRequest public class Json2CsharpRequest : TransformRequest
{ {
public string RootClassName { get; set; } = Json2CSharpTranslator.Context.DefaultRootClassName; public string RootClassName { get; } = Json2CSharpTranslator.Context.DefaultRootClassName;
public string Namespace { get; set; } = Json2CSharpTranslator.Context.DefaultNamespace; public string Namespace { get; } = Json2CSharpTranslator.Context.DefaultNamespace;
} }

View File

@ -1,6 +1,6 @@
namespace DevDisciples.Json.Tools.API.Requests; namespace DevDisciples.Json.Tools.API.Requests;
public class PrettifyRequest : TransformRequest public class PrettifyRequest : TransformRequest
{ {
public int IndentSize { get; set; } = JsonFormatter.Context.DefaultIndentSize; public int IndentSize { get; } = JsonFormatter.Context.DefaultIndentSize;
} }

View File

@ -1,6 +1,6 @@
namespace DevDisciples.Json.Tools.API.Requests; namespace DevDisciples.Json.Tools.API.Requests;
public abstract class TransformRequest public abstract class TransformRequest
{ {
public string Source { get; set; } = string.Empty; public string Source { get; init; } = string.Empty;
} }

View File

@ -1,5 +1,5 @@
namespace DevDisciples.Json.Tools.API.Requests; namespace DevDisciples.Json.Tools.API.Requests;
public class UglifyRequest : TransformRequest public class UglifyRequest : TransformRequest
{ {
} }

View File

@ -1 +1 @@
<p>home works!</p> <p>home works!</p>

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HomeComponent } from './home.component'; import { HomeComponent } from './home.component';
describe('HomeComponent', () => { describe('HomeComponent', () => {
let component: HomeComponent; let component: HomeComponent;
let fixture: ComponentFixture<HomeComponent>; let fixture: ComponentFixture<HomeComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [HomeComponent] imports: [HomeComponent]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(HomeComponent); fixture = TestBed.createComponent(HomeComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,12 +1,12 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
@Component({ @Component({
selector: 'app-home', selector: 'app-home',
standalone: true, standalone: true,
imports: [], imports: [],
templateUrl: './home.component.html', templateUrl: './home.component.html',
styleUrl: './home.component.scss' styleUrl: './home.component.scss'
}) })
export class HomeComponent { export class HomeComponent {
} }

View File

@ -1,15 +1,15 @@
<main> <main>
<section id="left"> <section id="left">
<h3>Input</h3> <h3>Input</h3>
<ngx-monaco-editor (ngModelChange)="handleInputChange($event)" <ngx-monaco-editor (ngModelChange)="handleInputChange($event)"
[options]="inputOptions" [options]="inputOptions"
[(ngModel)]="input"> [(ngModel)]="input">
</ngx-monaco-editor> </ngx-monaco-editor>
</section> </section>
<section id="right"> <section id="right">
<h3>Output</h3> <h3>Output</h3>
<ngx-monaco-editor [options]="outputOptions" <ngx-monaco-editor [options]="outputOptions"
[(ngModel)]="output"> [(ngModel)]="output">
</ngx-monaco-editor> </ngx-monaco-editor>
</section> </section>
</main> </main>

View File

@ -1,12 +1,12 @@
main { main {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
#left { #left {
flex: .5; flex: .5;
} }
#right { #right {
flex: .5; flex: .5;
} }
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { InputOutputComponent } from './input-output.component'; import { InputOutputComponent } from './input-output.component';
describe('InputOutputComponent', () => { describe('InputOutputComponent', () => {
let component: InputOutputComponent; let component: InputOutputComponent;
let fixture: ComponentFixture<InputOutputComponent>; let fixture: ComponentFixture<InputOutputComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [InputOutputComponent] imports: [InputOutputComponent]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(InputOutputComponent); fixture = TestBed.createComponent(InputOutputComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,25 +1,25 @@
import {Component, EventEmitter, Input, Output} from '@angular/core'; import {Component, EventEmitter, Input, Output} from '@angular/core';
import {EditorComponent} from "ngx-monaco-editor-v2"; import {EditorComponent} from "ngx-monaco-editor-v2";
import {FormsModule} from "@angular/forms"; import {FormsModule} from "@angular/forms";
@Component({ @Component({
selector: 'app-input-output', selector: 'app-input-output',
standalone: true, standalone: true,
imports: [ imports: [
EditorComponent, EditorComponent,
FormsModule FormsModule
], ],
templateUrl: './input-output.component.html', templateUrl: './input-output.component.html',
styleUrl: './input-output.component.scss' styleUrl: './input-output.component.scss'
}) })
export class InputOutputComponent { export class InputOutputComponent {
@Input() input!: string; @Input() input!: string;
@Input() inputOptions!: any; @Input() inputOptions!: any;
@Output() onInputChange = new EventEmitter(); @Output() onInputChange = new EventEmitter();
@Input() output!: string; @Input() output!: string;
@Input() outputOptions!: any; @Input() outputOptions!: any;
handleInputChange($event: string) { handleInputChange($event: string) {
this.onInputChange.emit($event); this.onInputChange.emit($event);
} }
} }

View File

@ -1,16 +1,16 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { JsonTransformService } from './json-transform.service'; import { JsonTransformService } from './json-transform.service';
describe('JsonTransformService', () => { describe('JsonTransformService', () => {
let service: JsonTransformService; let service: JsonTransformService;
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({}); TestBed.configureTestingModule({});
service = TestBed.inject(JsonTransformService); service = TestBed.inject(JsonTransformService);
}); });
it('should be created', () => { it('should be created', () => {
expect(service).toBeTruthy(); expect(service).toBeTruthy();
}); });
}); });

View File

@ -1,25 +1,25 @@
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {HttpClient, HttpResponse} from "@angular/common/http"; import {HttpClient, HttpResponse} from "@angular/common/http";
import {Observable} from "rxjs"; import {Observable} from "rxjs";
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class JsonTransformService { export class JsonTransformService {
private url: string = "https://localhost:5001"; private url: string = "https://localhost:5001";
constructor(private http: HttpClient) { constructor(private http: HttpClient) {
} }
public prettify(source: string): Observable<HttpResponse<any>> { public prettify(source: string): Observable<HttpResponse<any>> {
return this.http.post(`${this.url}/api/transform/prettify`, {source: source}, {observe: "response"}); return this.http.post(`${this.url}/api/transform/prettify`, {Source: source}, {observe: "response"});
} }
public uglify(source: string): Observable<HttpResponse<any>> { public uglify(source: string): Observable<HttpResponse<any>> {
return this.http.post(`${this.url}/api/transform/uglify`, {source: source}, {observe: "response"}); return this.http.post(`${this.url}/api/transform/uglify`, {Source: source}, {observe: "response"});
} }
public json2csharp(source: string): Observable<HttpResponse<any>> { public json2csharp(source: string): Observable<HttpResponse<any>> {
return this.http.post(`${this.url}/api/transform/json2csharp`, {source: source}, {observe: "response"}); return this.http.post(`${this.url}/api/transform/json2csharp`, {Source: source}, {observe: "response"});
} }
} }

View File

@ -1,8 +1,8 @@
<h1>JSON to C#</h1> <h1>JSON to C#</h1>
<app-input-output [input]="input" <app-input-output [input]="input"
[inputOptions]="inputOptions" [inputOptions]="inputOptions"
(onInputChange)="handleInputChange($event)" (onInputChange)="handleInputChange($event)"
[output]="output" [output]="output"
[outputOptions]="outputOptions"> [outputOptions]="outputOptions">
</app-input-output> </app-input-output>

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Json2CsharpComponent } from './json2-csharp.component'; import { Json2CsharpComponent } from './json2-csharp.component';
describe('Json2csharpComponent', () => { describe('Json2csharpComponent', () => {
let component: Json2CsharpComponent; let component: Json2CsharpComponent;
let fixture: ComponentFixture<Json2CsharpComponent>; let fixture: ComponentFixture<Json2CsharpComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [Json2CsharpComponent] imports: [Json2CsharpComponent]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(Json2CsharpComponent); fixture = TestBed.createComponent(Json2CsharpComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,55 +1,69 @@
import {Component, OnInit} from '@angular/core'; import {AfterViewInit, Component, OnInit} from '@angular/core';
import {InputOutputComponent} from "../input-output/input-output.component"; import {InputOutputComponent} from "../input-output/input-output.component";
import {JsonTransformService} from "../json-transform.service"; import {JsonTransformService} from "../json-transform.service";
@Component({ @Component({
selector: 'app-json2csharp', selector: 'app-json2csharp',
standalone: true, standalone: true,
imports: [ imports: [
InputOutputComponent InputOutputComponent
], ],
templateUrl: './json2-csharp.component.html', templateUrl: './json2-csharp.component.html',
styleUrl: './json2-csharp.component.scss' styleUrl: './json2-csharp.component.scss'
}) })
export class Json2CsharpComponent implements OnInit { export class Json2CsharpComponent implements AfterViewInit {
input: string = JSON.stringify({first_name: "John", last_name: "Doe"}, null, 2); input: string = JSON.stringify({first_name: "John", last_name: "Doe"}, null, 2);
inputOptions = {theme: 'vs-dark', language: 'json', readOnly: false}; inputOptions = {theme: 'vs-dark', language: 'json', readOnly: false};
output: string = JSON.stringify({first_name: "John", last_name: "Doe"}); output: string = JSON.stringify({first_name: "John", last_name: "Doe"});
outputOptions = {theme: 'vs-dark', language: 'csharp', readOnly: true}; outputOptions = {theme: 'vs-dark', language: 'csharp', readOnly: true};
constructor(private service: JsonTransformService) { constructor(private service: JsonTransformService) {
} }
ngOnInit(): void { ngAfterViewInit(): void {
this.service this.service
.json2csharp(this.input) .json2csharp(this.input)
.subscribe(response => { .subscribe(response => {
console.log(response); console.log(response);
if (response.status === 200) { if (response.status === 200) {
this.output = response.body.result; this.output = response.body.result;
} else if (response.status === 499) { } else if (response.status === 499) {
this.output = response.body.detail; this.output = response.body.detail;
} }
}); });
} }
handleInputChange($event: any) { ngOnInit(): void {
console.log($event); // this.service
this.service // .json2csharp(this.input)
.json2csharp($event) // .subscribe(response => {
.subscribe({ // console.log(response);
next: response => { //
console.log(response); // if (response.status === 200) {
this.output = response.body.result; // this.output = response.body.result;
}, // } else if (response.status === 499) {
error: response => { // this.output = response.body.detail;
console.log(response) // }
if (response.status === 499) { // });
this.output = response.error.detail; }
console.log(response.error.detail);
} handleInputChange($event: any) {
} console.log($event);
}); this.service
} .json2csharp($event)
} .subscribe({
next: response => {
console.log(response);
this.output = response.body.result;
},
error: response => {
console.log(response)
if (response.status === 499) {
this.output = response.error.detail;
console.log(response.error.detail);
}
}
});
}
}

View File

@ -1,25 +1,25 @@
<mat-sidenav-container class="sidenav-container"> <mat-sidenav-container class="sidenav-container">
<mat-sidenav #drawer class="sidenav" mode="side" opened> <mat-sidenav #drawer class="sidenav" mode="side" opened>
<mat-toolbar>Menu</mat-toolbar> <mat-toolbar>Menu</mat-toolbar>
<mat-nav-list> <mat-nav-list>
<mat-list-item routerLink="/home">Home</mat-list-item> <mat-list-item routerLink="/home">Home</mat-list-item>
<mat-list-item routerLink="/prettify">Prettify</mat-list-item> <mat-list-item routerLink="/prettify">Prettify</mat-list-item>
<mat-list-item routerLink="/uglify">Uglify</mat-list-item> <mat-list-item routerLink="/uglify">Uglify</mat-list-item>
<mat-list-item routerLink="/json2csharp">JSON to C#</mat-list-item> <mat-list-item routerLink="/json2csharp">JSON to C#</mat-list-item>
</mat-nav-list> </mat-nav-list>
</mat-sidenav> </mat-sidenav>
<mat-sidenav-content> <mat-sidenav-content>
<mat-toolbar color="primary"> <mat-toolbar color="primary">
<button mat-icon-button (click)="drawer.toggle()"> <button mat-icon-button (click)="drawer.toggle()">
<mat-icon>menu</mat-icon> <mat-icon>menu</mat-icon>
</button> </button>
<span>JSON Transform</span> <span>JSON Transform</span>
</mat-toolbar> </mat-toolbar>
<div class="content"> <div class="content">
<!-- Add your dashboard content here --> <!-- Add your dashboard content here -->
<router-outlet></router-outlet> <router-outlet></router-outlet>
</div> </div>
</mat-sidenav-content> </mat-sidenav-content>
</mat-sidenav-container> </mat-sidenav-container>

View File

@ -1,11 +1,11 @@
.sidenav-container { .sidenav-container {
height: 100%; height: 100%;
} }
.sidenav { .sidenav {
width: 250px; width: 250px;
} }
.content { .content {
padding: 16px; padding: 16px;
} }

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LayoutComponent } from './layout.component'; import { LayoutComponent } from './layout.component';
describe('LayoutComponent', () => { describe('LayoutComponent', () => {
let component: LayoutComponent; let component: LayoutComponent;
let fixture: ComponentFixture<LayoutComponent>; let fixture: ComponentFixture<LayoutComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [LayoutComponent] imports: [LayoutComponent]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(LayoutComponent); fixture = TestBed.createComponent(LayoutComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,27 +1,27 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import {MatSidenavContainer, MatSidenavModule} from "@angular/material/sidenav"; import {MatSidenavContainer, MatSidenavModule} from "@angular/material/sidenav";
import {MatToolbarModule} from "@angular/material/toolbar"; import {MatToolbarModule} from "@angular/material/toolbar";
import {MatListItem, MatNavList} from "@angular/material/list"; import {MatListItem, MatNavList} from "@angular/material/list";
import {RouterLink, RouterOutlet} from "@angular/router"; import {RouterLink, RouterOutlet} from "@angular/router";
import {MatIconButton} from "@angular/material/button"; import {MatIconButton} from "@angular/material/button";
import {MatIconModule} from "@angular/material/icon"; import {MatIconModule} from "@angular/material/icon";
@Component({ @Component({
selector: 'app-layout', selector: 'app-layout',
standalone: true, standalone: true,
imports: [ imports: [
MatSidenavContainer, MatSidenavContainer,
MatToolbarModule, MatToolbarModule,
MatNavList, MatNavList,
MatListItem, MatListItem,
MatSidenavModule, MatSidenavModule,
RouterLink, RouterLink,
MatIconButton, MatIconButton,
MatIconModule, MatIconModule,
RouterOutlet RouterOutlet
], ],
templateUrl: './layout.component.html', templateUrl: './layout.component.html',
styleUrl: './layout.component.scss' styleUrl: './layout.component.scss'
}) })
export class LayoutComponent { export class LayoutComponent {
} }

View File

@ -1,8 +1,8 @@
<h1>JSON Prettify</h1> <h1>JSON Prettify</h1>
<app-input-output [input]="input" <app-input-output [input]="input"
[inputOptions]="inputOptions" [inputOptions]="inputOptions"
(onInputChange)="handleInputChange($event)" (onInputChange)="handleInputChange($event)"
[output]="output" [output]="output"
[outputOptions]="outputOptions"> [outputOptions]="outputOptions">
</app-input-output> </app-input-output>

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PrettifyComponent } from './prettify.component'; import { PrettifyComponent } from './prettify.component';
describe('PrettifyComponent', () => { describe('PrettifyComponent', () => {
let component: PrettifyComponent; let component: PrettifyComponent;
let fixture: ComponentFixture<PrettifyComponent>; let fixture: ComponentFixture<PrettifyComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [PrettifyComponent] imports: [PrettifyComponent]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(PrettifyComponent); fixture = TestBed.createComponent(PrettifyComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,46 +1,46 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {EditorComponent} from "ngx-monaco-editor-v2"; import {EditorComponent} from "ngx-monaco-editor-v2";
import {FormsModule} from "@angular/forms"; import {FormsModule} from "@angular/forms";
import {InputOutputComponent} from "../input-output/input-output.component"; import {InputOutputComponent} from "../input-output/input-output.component";
import {JsonTransformService} from "../json-transform.service"; import {JsonTransformService} from "../json-transform.service";
@Component({ @Component({
selector: 'app-prettify', selector: 'app-prettify',
standalone: true, standalone: true,
imports: [ imports: [
EditorComponent, EditorComponent,
FormsModule, FormsModule,
InputOutputComponent InputOutputComponent
], ],
templateUrl: './prettify.component.html', templateUrl: './prettify.component.html',
styleUrl: './prettify.component.scss' styleUrl: './prettify.component.scss'
}) })
export class PrettifyComponent { export class PrettifyComponent {
input: string = JSON.stringify({first_name: "John", last_name: "Doe"}); input: string = JSON.stringify({first_name: "John", last_name: "Doe"});
inputOptions = {theme: 'vs-dark', language: 'json', readOnly: false}; inputOptions = {theme: 'vs-dark', language: 'json', readOnly: false};
output: string = JSON.stringify({first_name: "John", last_name: "Doe"}, null, 2); output: string = JSON.stringify({first_name: "John", last_name: "Doe"}, null, 2);
outputOptions = {theme: 'vs-dark', language: 'json', readOnly: true}; outputOptions = {theme: 'vs-dark', language: 'json', readOnly: true};
error: string = ""; error: string = "";
constructor(private service: JsonTransformService) { constructor(private service: JsonTransformService) {
} }
handleInputChange($event: any) { handleInputChange($event: any) {
console.log($event); console.log($event);
this.service this.service
.prettify($event) .prettify($event)
.subscribe({ .subscribe({
next: response => { next: response => {
console.log(response); console.log(response);
this.output = response.body.result; this.output = response.body.result;
}, },
error: response => { error: response => {
console.log(response) console.log(response)
if (response.status === 499) { if (response.status === 499) {
this.output = response.error.detail; this.output = response.error.detail;
console.log(response.error.detail); console.log(response.error.detail);
} }
} }
}); });
} }
} }

View File

@ -1,8 +1,8 @@
<h1>JSON Uglify</h1> <h1>JSON Uglify</h1>
<app-input-output [input]="input" <app-input-output [input]="input"
[inputOptions]="inputOptions" [inputOptions]="inputOptions"
(onInputChange)="handleInputChange($event)" (onInputChange)="handleInputChange($event)"
[output]="output" [output]="output"
[outputOptions]="outputOptions"> [outputOptions]="outputOptions">
</app-input-output> </app-input-output>

View File

@ -1,23 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UglifyComponent } from './uglify.component'; import { UglifyComponent } from './uglify.component';
describe('UglifyComponent', () => { describe('UglifyComponent', () => {
let component: UglifyComponent; let component: UglifyComponent;
let fixture: ComponentFixture<UglifyComponent>; let fixture: ComponentFixture<UglifyComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [UglifyComponent] imports: [UglifyComponent]
}) })
.compileComponents(); .compileComponents();
fixture = TestBed.createComponent(UglifyComponent); fixture = TestBed.createComponent(UglifyComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
}); });

View File

@ -1,41 +1,41 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {JsonTransformService} from "../json-transform.service"; import {JsonTransformService} from "../json-transform.service";
import {InputOutputComponent} from "../input-output/input-output.component"; import {InputOutputComponent} from "../input-output/input-output.component";
@Component({ @Component({
selector: 'app-uglify', selector: 'app-uglify',
standalone: true, standalone: true,
imports: [ imports: [
InputOutputComponent InputOutputComponent
], ],
templateUrl: './uglify.component.html', templateUrl: './uglify.component.html',
styleUrl: './uglify.component.scss' styleUrl: './uglify.component.scss'
}) })
export class UglifyComponent { export class UglifyComponent {
input: string = JSON.stringify({first_name: "John", last_name: "Doe"}, null, 2); input: string = JSON.stringify({first_name: "John", last_name: "Doe"}, null, 2);
inputOptions = {theme: 'vs-dark', language: 'json', readOnly: false}; inputOptions = {theme: 'vs-dark', language: 'json', readOnly: false};
output: string = JSON.stringify({first_name: "John", last_name: "Doe"}); output: string = JSON.stringify({first_name: "John", last_name: "Doe"});
outputOptions = {theme: 'vs-dark', language: 'json', readOnly: true}; outputOptions = {theme: 'vs-dark', language: 'json', readOnly: true};
constructor(private service: JsonTransformService) { constructor(private service: JsonTransformService) {
} }
handleInputChange($event: any) { handleInputChange($event: any) {
console.log($event); console.log($event);
this.service this.service
.uglify($event) .uglify($event)
.subscribe({ .subscribe({
next: response => { next: response => {
console.log(response); console.log(response);
this.output = response.body.result; this.output = response.body.result;
}, },
error: response => { error: response => {
console.log(response) console.log(response)
if (response.status === 499) { if (response.status === 499) {
this.output = response.error.detail; this.output = response.error.detail;
console.log(response.error.detail); console.log(response.error.detail);
} }
} }
}); });
} }
} }

View File

@ -4,7 +4,7 @@ public partial class Json2CSharpCommand
{ {
public class CommandOptions : CLI.CommandOptions public class CommandOptions : CLI.CommandOptions
{ {
public string? RootClassName { get; set; } public string? RootClassName { get; init; }
public string? Namespace { get; set; } public string? Namespace { get; init; }
} }
} }

View File

@ -4,6 +4,6 @@ public partial class JsonPrettifyCommand
{ {
public class CommandOptions : CLI.CommandOptions public class CommandOptions : CLI.CommandOptions
{ {
public int IndentSize { get; set; } public int IndentSize { get; init; }
} }
} }

View File

@ -4,10 +4,10 @@ namespace DevDisciples.Json.Tools;
public static partial class Json2CSharpTranslator public static partial class Json2CSharpTranslator
{ {
public struct ClassTranslation : ITranslation public readonly struct ClassTranslation : ITranslation
{ {
public string Name { get; set; } public string Name { get; init; }
public List<PropertyTranslation> Properties { get; set; } public List<PropertyTranslation> Properties { get; init; }
public void Translate(Context context) public void Translate(Context context)
{ {

View File

@ -9,9 +9,9 @@ public static partial class Json2CSharpTranslator
public const string DefaultRootClassName = "Root"; public const string DefaultRootClassName = "Root";
public const string DefaultNamespace = "My.Namespace"; public const string DefaultNamespace = "My.Namespace";
public string RootClassName { get; set; } = DefaultRootClassName; public string RootClassName { get; init; } = DefaultRootClassName;
public string Namespace { get; set; } = DefaultNamespace; public string Namespace { get; init; } = DefaultNamespace;
public List<ClassTranslation> Classes { get; set; } = new(); public List<ClassTranslation> Classes { get; } = new();
public readonly StringBuilder Builder = new(); public readonly StringBuilder Builder = new();
} }
} }

View File

@ -40,7 +40,7 @@ public static partial class Json2CSharpTranslator
}; };
} }
private static ClassTranslation SquashObjects(string className, List<ISyntaxNode> objects, object[] args) private static ClassTranslation SquashObjects(string className, IEnumerable<ISyntaxNode> objects, object[] args)
{ {
var classes = objects var classes = objects
.Select(@object => JsonObjectTranslator.Translate(@object, args)) .Select(@object => JsonObjectTranslator.Translate(@object, args))

View File

@ -4,10 +4,10 @@ namespace DevDisciples.Json.Tools;
public static partial class Json2CSharpTranslator public static partial class Json2CSharpTranslator
{ {
public struct PropertyTranslation : ITranslation public readonly struct PropertyTranslation : ITranslation
{ {
public string Name { get; set; } public string Name { get; init; }
public string Type { get; set; } public string Type { get; init; }
public void Translate(Context context) public void Translate(Context context)
{ {

View File

@ -9,10 +9,10 @@ public static partial class JsonFormatter
public const int DefaultIndentSize = 2; public const int DefaultIndentSize = 2;
protected int Depth { get; set; } = 0; protected int Depth { get; set; } = 0;
public StringBuilder Builder { get; set; } = new(); public StringBuilder Builder { get; } = new();
public bool Beautify { get; set; } = false; public bool Beautify { get; init; } = false;
public string Indent => new(' ', Depth); public string Indent => new(' ', Depth);
public int IndentSize { get; set; } = DefaultIndentSize; public int IndentSize { get; init; } = DefaultIndentSize;
public string NewLine => Beautify ? "\n" : ""; public string NewLine => Beautify ? "\n" : "";
public string Space => Beautify ? " " : ""; public string Space => Beautify ? " " : "";

View File

@ -44,12 +44,12 @@ public static partial class JsonFormatter
context.Builder.Append($"[{context.NewLine}"); context.Builder.Append($"[{context.NewLine}");
context.IncrementDepth(); context.IncrementDepth();
for (var i = 0; i < array.Elements.Count; i++) for (var i = 0; i < array.Elements.Length; i++)
{ {
var node = array.Elements[i]; var node = array.Elements[i];
context.Builder.Append(context.Indent); context.Builder.Append(context.Indent);
Visitors[node.GetType()](node, args); Visitors[node.GetType()](node, args);
if (i < array.Elements.Count - 1) context.Builder.Append($",{context.NewLine}"); if (i < array.Elements.Length - 1) context.Builder.Append($",{context.NewLine}");
} }
context.DecrementDepth(); context.DecrementDepth();
@ -64,12 +64,12 @@ public static partial class JsonFormatter
context.Builder.Append($"{{{context.NewLine}"); context.Builder.Append($"{{{context.NewLine}");
context.IncrementDepth(); context.IncrementDepth();
var count = @object.Properties.Count; var count = @object.Properties.Length;
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
{ {
var (key, node) = @object.Properties.ElementAt(i); var property = @object.Properties.ElementAt(i);
context.Builder.Append($"{context.Indent}\"{key}\":{context.Space}"); context.Builder.Append($"{context.Indent}\"{property.Key}\":{context.Space}");
Visitors[node.GetType()](node, args); Visitors[property.Value.GetType()](property.Value, args);
if (i < count - 1) context.Builder.Append($",{context.NewLine}"); if (i < count - 1) context.Builder.Append($",{context.NewLine}");
} }

View File

@ -2,7 +2,7 @@
public abstract partial class Lexer<TToken> where TToken : Enum public abstract partial class Lexer<TToken> where TToken : Enum
{ {
public struct Token : ISourceLocation public readonly struct Token : ISourceLocation
{ {
public string File { get; } public string File { get; }
public TToken Type { get; } public TToken Type { get; }

View File

@ -16,7 +16,7 @@ public class VisitorContainer
public class VisitorContainer<T> public class VisitorContainer<T>
{ {
protected Dictionary<Type, Visitor.Visit<T>> Visitors { get; } = new(); protected Dictionary<Type, Visitor.Visit<T>> Visitors { get; } = new();
public Visitor.Visit<T> Default { get; set; } = default!; public Visitor.Visit<T> Default { get; } = default!;
public VisitorContainer<T> Register<TVisitee>(Visitor.Visit<T> visitor) public VisitorContainer<T> Register<TVisitee>(Visitor.Visit<T> visitor)
@ -31,7 +31,7 @@ public class VisitorContainer<T>
public class VisitorContainer<TIn, TOut> public class VisitorContainer<TIn, TOut>
{ {
protected Dictionary<Type, Visitor.Visit<TIn, TOut>> Visitors { get; } = new(); protected Dictionary<Type, Visitor.Visit<TIn, TOut>> Visitors { get; } = new();
public Visitor.Visit<TIn, TOut> Default { get; set; } = default!; public Visitor.Visit<TIn, TOut> Default { get; } = default!;
public void Register<TVisitee>(Visitor.Visit<TIn, TOut> visitor) public void Register<TVisitee>(Visitor.Visit<TIn, TOut> visitor)
{ {