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 },
JsonBool { Value: true },
JsonObject { Properties: null},
JsonArray { Elements: null },
JsonObject { Properties.Length: 0 },
JsonArray { Elements.Length: 0 },
JsonNull
]
});
@ -98,19 +98,19 @@ public class JsonParserTests
{
const string 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]
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);
Assert.That(node is JsonObject { Properties.Count: 1 });
Assert.That(node is JsonObject { Properties.Length: 1 });
var @object = (JsonObject)node;
Assert.That(@object.Properties.ContainsKey("first_name"));
Assert.That(@object.Properties["first_name"] is JsonString { Value: "John" });
Assert.That(@object.Properties.Any(property => property.Key == "first_name"));
Assert.That(@object.Properties.First(property => property.Key == "first_name").Value is JsonString { Value: "John" });
}
[Test]
@ -121,12 +121,12 @@ public class JsonParserTests
Assert.Multiple(() =>
{
Assert.That(node is JsonObject { Properties.Count: 2 });
Assert.That(node is JsonObject { Properties.Length: 2 });
var @object = (JsonObject)node;
Assert.That(@object.Properties.ContainsKey("first_name"));
Assert.That(@object.Properties["first_name"] is JsonString { Value: "John" });
Assert.That(@object.Properties.ContainsKey("last_name"));
Assert.That(@object.Properties["last_name"] is JsonString { Value: "Doe" });
Assert.That(@object.Properties.Any(property => property.Key == "first_name"));
Assert.That(@object.Properties.First(property => property.Key == "first_name").Value is JsonString { Value: "John" });
Assert.That(@object.Properties.Any(property => property.Key == "last_name"));
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;
public struct JsonArray : ISyntaxNode
public readonly struct JsonArray : ISyntaxNode
{
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;
Elements = elements ?? new();
Elements = elements;
}
}

View File

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

View File

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

View File

@ -2,7 +2,7 @@ using DevDisciples.Parsing;
namespace DevDisciples.Json.Parser;
public struct JsonNumber : ISyntaxNode
public readonly struct JsonNumber : ISyntaxNode
{
public Lexer<JsonToken>.Token Token { get; }
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;
public struct JsonObject : ISyntaxNode
public readonly partial struct JsonObject : ISyntaxNode
{
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;
Properties = properties ?? new();
Properties = properties;
}
}

View File

@ -51,7 +51,7 @@ public static partial class JsonParser
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)
@ -72,7 +72,9 @@ public static partial class JsonParser
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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
namespace DevDisciples.Json.Tools.API.Requests;
public class UglifyRequest : TransformRequest
{
namespace DevDisciples.Json.Tools.API.Requests;
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 { HomeComponent } from './home.component';
describe('HomeComponent', () => {
let component: HomeComponent;
let fixture: ComponentFixture<HomeComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [HomeComponent]
})
.compileComponents();
fixture = TestBed.createComponent(HomeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HomeComponent } from './home.component';
describe('HomeComponent', () => {
let component: HomeComponent;
let fixture: ComponentFixture<HomeComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [HomeComponent]
})
.compileComponents();
fixture = TestBed.createComponent(HomeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,55 +1,69 @@
import {Component, OnInit} from '@angular/core';
import {InputOutputComponent} from "../input-output/input-output.component";
import {JsonTransformService} from "../json-transform.service";
@Component({
selector: 'app-json2csharp',
standalone: true,
imports: [
InputOutputComponent
],
templateUrl: './json2-csharp.component.html',
styleUrl: './json2-csharp.component.scss'
})
export class Json2CsharpComponent implements OnInit {
input: string = JSON.stringify({first_name: "John", last_name: "Doe"}, null, 2);
inputOptions = {theme: 'vs-dark', language: 'json', readOnly: false};
output: string = JSON.stringify({first_name: "John", last_name: "Doe"});
outputOptions = {theme: 'vs-dark', language: 'csharp', readOnly: true};
constructor(private service: JsonTransformService) {
}
ngOnInit(): void {
this.service
.json2csharp(this.input)
.subscribe(response => {
console.log(response);
if (response.status === 200) {
this.output = response.body.result;
} else if (response.status === 499) {
this.output = response.body.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);
}
}
});
}
}
import {AfterViewInit, Component, OnInit} from '@angular/core';
import {InputOutputComponent} from "../input-output/input-output.component";
import {JsonTransformService} from "../json-transform.service";
@Component({
selector: 'app-json2csharp',
standalone: true,
imports: [
InputOutputComponent
],
templateUrl: './json2-csharp.component.html',
styleUrl: './json2-csharp.component.scss'
})
export class Json2CsharpComponent implements AfterViewInit {
input: string = JSON.stringify({first_name: "John", last_name: "Doe"}, null, 2);
inputOptions = {theme: 'vs-dark', language: 'json', readOnly: false};
output: string = JSON.stringify({first_name: "John", last_name: "Doe"});
outputOptions = {theme: 'vs-dark', language: 'csharp', readOnly: true};
constructor(private service: JsonTransformService) {
}
ngAfterViewInit(): void {
this.service
.json2csharp(this.input)
.subscribe(response => {
console.log(response);
if (response.status === 200) {
this.output = response.body.result;
} else if (response.status === 499) {
this.output = response.body.detail;
}
});
}
ngOnInit(): void {
// this.service
// .json2csharp(this.input)
// .subscribe(response => {
// console.log(response);
//
// if (response.status === 200) {
// this.output = response.body.result;
// } else if (response.status === 499) {
// this.output = response.body.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 #drawer class="sidenav" mode="side" opened>
<mat-toolbar>Menu</mat-toolbar>
<mat-nav-list>
<mat-list-item routerLink="/home">Home</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="/json2csharp">JSON to C#</mat-list-item>
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content>
<mat-toolbar color="primary">
<button mat-icon-button (click)="drawer.toggle()">
<mat-icon>menu</mat-icon>
</button>
<span>JSON Transform</span>
</mat-toolbar>
<div class="content">
<!-- Add your dashboard content here -->
<router-outlet></router-outlet>
</div>
</mat-sidenav-content>
</mat-sidenav-container>
<mat-sidenav-container class="sidenav-container">
<mat-sidenav #drawer class="sidenav" mode="side" opened>
<mat-toolbar>Menu</mat-toolbar>
<mat-nav-list>
<mat-list-item routerLink="/home">Home</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="/json2csharp">JSON to C#</mat-list-item>
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content>
<mat-toolbar color="primary">
<button mat-icon-button (click)="drawer.toggle()">
<mat-icon>menu</mat-icon>
</button>
<span>JSON Transform</span>
</mat-toolbar>
<div class="content">
<!-- Add your dashboard content here -->
<router-outlet></router-outlet>
</div>
</mat-sidenav-content>
</mat-sidenav-container>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

@ -4,6 +4,6 @@ public partial class JsonPrettifyCommand
{
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 struct ClassTranslation : ITranslation
public readonly struct ClassTranslation : ITranslation
{
public string Name { get; set; }
public List<PropertyTranslation> Properties { get; set; }
public string Name { get; init; }
public List<PropertyTranslation> Properties { get; init; }
public void Translate(Context context)
{

View File

@ -9,9 +9,9 @@ public static partial class Json2CSharpTranslator
public const string DefaultRootClassName = "Root";
public const string DefaultNamespace = "My.Namespace";
public string RootClassName { get; set; } = DefaultRootClassName;
public string Namespace { get; set; } = DefaultNamespace;
public List<ClassTranslation> Classes { get; set; } = new();
public string RootClassName { get; init; } = DefaultRootClassName;
public string Namespace { get; init; } = DefaultNamespace;
public List<ClassTranslation> Classes { get; } = 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
.Select(@object => JsonObjectTranslator.Translate(@object, args))

View File

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

View File

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

View File

@ -44,12 +44,12 @@ public static partial class JsonFormatter
context.Builder.Append($"[{context.NewLine}");
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];
context.Builder.Append(context.Indent);
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();
@ -64,12 +64,12 @@ public static partial class JsonFormatter
context.Builder.Append($"{{{context.NewLine}");
context.IncrementDepth();
var count = @object.Properties.Count;
var count = @object.Properties.Length;
for (var i = 0; i < count; i++)
{
var (key, node) = @object.Properties.ElementAt(i);
context.Builder.Append($"{context.Indent}\"{key}\":{context.Space}");
Visitors[node.GetType()](node, args);
var property = @object.Properties.ElementAt(i);
context.Builder.Append($"{context.Indent}\"{property.Key}\":{context.Space}");
Visitors[property.Value.GetType()](property.Value, args);
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 struct Token : ISourceLocation
public readonly struct Token : ISourceLocation
{
public string File { get; }
public TToken Type { get; }

View File

@ -16,7 +16,7 @@ public class VisitorContainer
public class VisitorContainer<T>
{
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)
@ -31,7 +31,7 @@ public class VisitorContainer<T>
public class VisitorContainer<TIn, TOut>
{
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)
{