Skip to content

Commit 014926a

Browse files
committed
it's working; it's working
1 parent c6bc4a4 commit 014926a

14 files changed

+160
-71
lines changed

Diff for: LearnJsonEverything.Template/ILessonRunner.cs

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
namespace LearnJsonEverything.Template;
1+
// ReSharper disable CheckNamespace
2+
3+
using System.Text.Json.Nodes;
4+
5+
namespace LearnJsonEverything;
26

37
public interface ILessonRunner<out T>
48
{
5-
T Run();
9+
T Run(JsonObject context);
610
}

Diff for: LearnJsonEverything.Template/LearnJsonEverything.Template.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net6.0</TargetFramework>
4+
<TargetFramework>net8.0</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
</PropertyGroup>

Diff for: LearnJsonEverything/LearnJsonEverything.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<NoWarn>NU1701</NoWarn>
88
<PublishTrimmed>false</PublishTrimmed>
99
<LangVersion>latest</LangVersion>
10+
<WasmEnableWebcil>false</WasmEnableWebcil>
1011
</PropertyGroup>
1112

1213
<ItemGroup>

Diff for: LearnJsonEverything/Pages/Schema.razor

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
@page "/json-schema"
22

3-
<Teacher LessonSource="/data/lessons/schema.json"></Teacher>
3+
<Teacher LessonSource="/data/lessons/schema.yaml"></Teacher>
44

55
@code {
66

Diff for: LearnJsonEverything/Services/EditorOptions.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,24 @@ namespace LearnJsonEverything.Services
44
{
55
public static class EditorOptions
66
{
7-
public static StandaloneEditorConstructionOptions Basic() =>
7+
public static StandaloneEditorConstructionOptions Basic(string lang, int tab) =>
88
new()
99
{
1010
AutomaticLayout = true,
11-
Language = "csharp",
11+
Language = lang,
1212
Theme = "vs-dark",
1313
SelectOnLineNumbers = true,
1414
Scrollbar = new EditorScrollbarOptions
1515
{
1616
AlwaysConsumeMouseWheel = false
1717
},
1818
ScrollBeyondLastLine = false,
19-
TabSize = 4
19+
TabSize = tab
2020
};
2121

22-
public static StandaloneEditorConstructionOptions Readonly()
22+
public static StandaloneEditorConstructionOptions Readonly(string lang, int tab)
2323
{
24-
var options = Basic();
24+
var options = Basic(lang, tab);
2525
options.ReadOnly = true;
2626

2727
return options;

Diff for: LearnJsonEverything/Services/LessonData.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ public class LessonData
88
public Guid Id { get; set; }
99
public string Title { get; set; }
1010
public string Instructions { get; set; }
11-
public JsonObject? Data { get; set; }
11+
public string ContextCode { get; set; }
12+
public JsonArray? Tests { get; set; }
1213
}

Diff for: LearnJsonEverything/Services/Runners/SchemaRunner.cs

+74-38
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,22 @@
11
using System.Reflection;
22
using System.Text.Json;
3+
using System.Text.Json.Nodes;
4+
using Json.More;
35
using Json.Schema;
4-
using LearnJsonEverything.Template;
56
using Microsoft.CodeAnalysis;
67
using Microsoft.CodeAnalysis.CSharp;
78

9+
using static LearnJsonEverything.Services.SerializationHelpers;
10+
811
namespace LearnJsonEverything.Services.Runners;
912

1013
public static class SchemaRunner
1114
{
12-
private const string ContextCode =
13-
"""
14-
using System;
15-
using System.Collections.Generic;
16-
using System.Text.Json;
17-
using System.Text.Json.Nodes;
18-
using System.Text.Json.Serialization;
19-
using Json.Schema;
20-
21-
namespace JsonEverythingTemp;
22-
23-
public class Lesson : ILessonRunner<EvaluationResults>
24-
{
25-
public EvaluationResults Run()
26-
{
27-
var instance = JsonNode.Parse("/* INSTANCE */");
28-
29-
/* USER CODE */
30-
}
31-
}
32-
""";
15+
private class SchemaTest
16+
{
17+
public JsonNode? Instance { get; set; }
18+
public bool IsValid { get; set; }
19+
}
3320

3421
private const string Instructions =
3522
$"""
@@ -40,20 +27,50 @@ public EvaluationResults Run()
4027
### Code template
4128
4229
```csharp
43-
{ContextCode}
30+
/* CONTEXT CODE */
4431
```
32+
33+
### Tests
34+
/* TESTS */
35+
4536
""";
4637

38+
private const string SuccessIcon = "✔";
39+
private const string ErrorIcon = "❌";
40+
private const string WarnIcon = "⚠";
41+
private const string MessageIcon = "ⓘ";
42+
43+
//public static string ErrorIcon = "<span style=\"foreground:red;\">❌</span>";
44+
//public static string WarnIcon = "<span style=\"foreground:amber;\">⚠</span>";
45+
//public static string MessageIcon = "<span style=\"foreground:lightblue;\">ⓘ</span>";
46+
4747
public static string BuildInstructions(LessonData lesson) => Instructions
4848
.Replace("/* TITLE */", lesson.Title)
4949
.Replace("/* INSTRUCTIONS */", lesson.Instructions)
50-
.Replace("/* INSTANCE */", JsonSerializer.Serialize(lesson.Data?["instance"]));
50+
.Replace("/* CONTEXT CODE */", lesson.ContextCode)
51+
.Replace("/* TESTS */", BuildTestList(lesson.Tests));
5152

52-
public static string Run(string userCode, LessonData lesson, MetadataReference[] references)
53+
private static string BuildTestList(JsonArray testData)
5354
{
54-
var fullSource = ContextCode
55-
.Replace("/* USER CODE */", userCode)
56-
.Replace("/* INSTANCE */", JsonSerializer.Serialize(lesson.Data?["instance"]));
55+
var tests = testData.Deserialize<SchemaTest[]>(SerializerOptions);
56+
var lines = new List<string>
57+
{
58+
"| Instance | Is Valid |",
59+
"|:-|:-:|"
60+
};
61+
62+
foreach (var test in tests)
63+
{
64+
lines.Add($"|`{test.Instance.AsJsonString()}`|{test.IsValid}|");
65+
}
66+
67+
return string.Join(Environment.NewLine, lines);
68+
}
69+
70+
public static string[] Run(string userCode, LessonData lesson, MetadataReference[] references)
71+
{
72+
var fullSource = lesson.ContextCode
73+
.Replace("/* USER CODE */", userCode);
5774

5875
var syntaxTree = CSharpSyntaxTree.ParseText(fullSource);
5976
var assemblyPath = Path.ChangeExtension(Path.GetTempFileName(), "dll");
@@ -69,24 +86,43 @@ public static string Run(string userCode, LessonData lesson, MetadataReference[]
6986
var emitResult = compilation.Emit(dllStream, pdbStream, xmlStream);
7087
if (!emitResult.Success)
7188
{
72-
Console.WriteLine("You may expect a list of what compilation errors there are, but unfortunately " +
73-
"Roslyn doesn't seem to be giving that information out (or I don't know how to " +
74-
"interpret it). So instead, here are the errors in raw form. Good luck. If you " +
75-
"know what these mean, please drop a line in a GitHub issue.");
76-
//var errors = string.Join("\n", emitResult.Diagnostics.Where(x => x.Severity == DiagnosticSeverity.Error)
77-
// .Select(x => GetErrorDetails(source, x)));
78-
//await _outputEditor.SetValue(errors);
79-
return "Compilation error";
89+
var diagnostics = new List<string>();
90+
foreach (var diagnostic in emitResult.Diagnostics)
91+
{
92+
var icon = diagnostic.Severity switch
93+
{
94+
DiagnosticSeverity.Info => MessageIcon,
95+
DiagnosticSeverity.Warning => WarnIcon,
96+
DiagnosticSeverity.Error => ErrorIcon,
97+
_ => string.Empty
98+
};
99+
diagnostics.Add($"{icon} {diagnostic.GetMessage()}");
100+
}
101+
return [.. diagnostics];
80102
}
81103

82104
var assembly = Assembly.Load(dllStream.ToArray());
83105

84106
var type = assembly.DefinedTypes.Single(x => !x.IsInterface && x.ImplementedInterfaces.Contains(typeof(ILessonRunner<EvaluationResults>)));
85107
var runner = (ILessonRunner<EvaluationResults>) Activator.CreateInstance(type)!;
86108

87-
var results = runner.Run();
109+
var tests = lesson.Tests.Deserialize<SchemaTest[]>(SerializerOptions);
110+
var results = new List<string>();
111+
112+
foreach (var test in tests)
113+
{
114+
var result = runner.Run(new JsonObject { ["instance"] = test.Instance });
115+
results.Add($"{(test.IsValid == result.IsValid ? SuccessIcon : ErrorIcon)} {test.Instance.AsJsonString()}");
116+
}
117+
88118
// run the code
89119

90-
return JsonSerializer.Serialize(results);
120+
return results.ToArray();
121+
}
122+
123+
private static string ToLiteral(this string valueTextForCompiler)
124+
{
125+
var formatted = SymbolDisplay.FormatLiteral(valueTextForCompiler, true);
126+
return formatted;
91127
}
92128
}

Diff for: LearnJsonEverything/Services/SerializationHelpers.cs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System.Text.Encodings.Web;
2+
using System.Text.Json;
3+
4+
namespace LearnJsonEverything.Services
5+
{
6+
public static class SerializationHelpers
7+
{
8+
public static readonly JsonSerializerOptions SerializerOptions = new()
9+
{
10+
WriteIndented = true,
11+
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
12+
PropertyNameCaseInsensitive = true
13+
};
14+
}
15+
}

Diff for: LearnJsonEverything/Shared/MarkdownSpan.razor.css

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pre {
88
padding: 9.5px;
99
margin: 0 0 10px;
1010
font-size: 13px;
11+
tab-size: 4;
1112
word-break: break-all;
1213
word-wrap: break-word;
1314
background-color: #f0f0f0;

Diff for: LearnJsonEverything/Shared/Teacher.razor

+16-11
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
@using Microsoft.CodeAnalysis
55
@using Microsoft.CodeAnalysis.CSharp
66
@using System.Reflection
7+
@using System.Text.Encodings.Web
78
@using System.Text.Json
9+
@using Json.More
810
@using LearnJsonEverything.Services.Runners
11+
@using Yaml2JsonNode
912
@using EditorOptions = LearnJsonEverything.Services.EditorOptions
1013

1114
@inject DataManager DataManager;
@@ -23,7 +26,7 @@
2326
<div id="right-panel" class="row row-margin-reset flex-grow-1">
2427
<div id="workspace" class="col-12 d-flex flex-grow-1">
2528
<div class="grid-panel flex-grow-1">
26-
<StandaloneCodeEditor @ref="_codeEditor" Id="editor-schema" ConstructionOptions="BasicOptions"
29+
<StandaloneCodeEditor @ref="_codeEditor" Id="editor-schema" ConstructionOptions="OptionsForEditor"
2730
CssClass="h-100"></StandaloneCodeEditor>
2831
</div>
2932
</div>
@@ -36,7 +39,7 @@
3639
</div>
3740
<div id="output" class="col-12 d-flex">
3841
<div class="grid-panel flex-grow-1">
39-
<StandaloneCodeEditor @ref="_outputEditor" Id="editor-output" ConstructionOptions="ReadOnlyOptions"
42+
<StandaloneCodeEditor @ref="_outputEditor" Id="editor-output" ConstructionOptions="OptionsForOutput"
4043
CssClass="h-100"></StandaloneCodeEditor>
4144
</div>
4245
</div>
@@ -57,8 +60,8 @@
5760

5861
private string Instructions { get; set; }
5962

60-
private static StandaloneEditorConstructionOptions BasicOptions(StandaloneCodeEditor _) => EditorOptions.Basic();
61-
private static StandaloneEditorConstructionOptions ReadOnlyOptions(StandaloneCodeEditor _) => EditorOptions.Readonly();
63+
private static StandaloneEditorConstructionOptions OptionsForEditor(StandaloneCodeEditor _) => EditorOptions.Basic("csharp", 4);
64+
private static StandaloneEditorConstructionOptions OptionsForOutput(StandaloneCodeEditor _) => EditorOptions.Readonly("json", 2);
6265
#pragma warning restore CS8618
6366

6467
private async Task Run()
@@ -67,11 +70,9 @@
6770
{
6871
var userCode = await _codeEditor.GetValue();
6972

70-
_outputEditor.SetValue("");
71-
var output = SchemaRunner.Run(userCode, _currentLesson!, _references);
72-
73-
await _outputEditor.SetValue(output);
74-
73+
await _outputEditor.SetValue("");
74+
var results = SchemaRunner.Run(userCode, _currentLesson!, _references);
75+
await _outputEditor.SetValue(string.Join(Environment.NewLine, results!));
7576
}
7677
catch (Exception e)
7778
{
@@ -127,7 +128,7 @@
127128
int i = 0;
128129
foreach (var reference in refs.Where(x => !x.IsDynamic))
129130
{
130-
var source = $"/_framework/{reference.FullName!.Split(',')[0]}.wasm";
131+
var source = $"/_framework/{reference.FullName!.Split(',')[0]}.dll";
131132
try
132133
{
133134
var stream = await Client.GetStreamAsync(source);
@@ -146,6 +147,10 @@
146147

147148
private async Task DownloadLessonPlan()
148149
{
149-
_lessons = (await Client.GetFromJsonAsync<LessonPlan>(LessonSource))!;
150+
var yamlText = await Client.GetStringAsync(LessonSource);
151+
var yaml = YamlSerializer.Parse(yamlText);
152+
var json = yaml.ToJsonNode().FirstOrDefault();
153+
Console.WriteLine(json.AsJsonString());
154+
_lessons = json.Deserialize<LessonPlan>(SerializerOptions)!;
150155
}
151156
}

Diff for: LearnJsonEverything/_Imports.razor

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@
66
@using Microsoft.AspNetCore.Components.Web.Virtualization
77
@using Microsoft.AspNetCore.Components.WebAssembly.Http
88
@using Microsoft.JSInterop
9-
@using LearnJsonEverything.Shared
9+
@using LearnJsonEverything.Shared
10+
11+
@using static LearnJsonEverything.Services.SerializationHelpers

Diff for: LearnJsonEverything/wwwroot/css/app.css

+1
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ a.navbar-brand {
286286
pre {
287287
padding: 9.5px;
288288
font-size: 13px;
289+
tab-size: 4;
289290
word-break: break-all;
290291
word-wrap: break-word;
291292
background-color: rgb(27, 27, 30);

Diff for: LearnJsonEverything/wwwroot/data/lessons/schema.json

-11
This file was deleted.

Diff for: LearnJsonEverything/wwwroot/data/lessons/schema.yaml

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
- id: 26b6ebca-58e6-4814-86ea-4946d844c9a6
3+
title: Validate a string
4+
instructions: Create a _JsonSchema_ object and validate the string instance. Return
5+
the evaluation results.
6+
inputTemplate: ''
7+
contextCode: |-
8+
using System;
9+
using System.Collections.Generic;
10+
using System.Text.Json;
11+
using System.Text.Json.Nodes;
12+
using System.Text.Json.Serialization;
13+
using Json.Schema;
14+
15+
namespace LearnJsonEverything;
16+
17+
public class Lesson : ILessonRunner<EvaluationResults>
18+
{
19+
public EvaluationResults Run(JsonObject context)
20+
{
21+
var instance = context["instance"];
22+
23+
/* USER CODE */
24+
}
25+
}
26+
tests:
27+
- instance: a string value
28+
isValid: true
29+
- instance: []
30+
isValid: false
31+
- instance: 42
32+
isValid: false
33+
- instance: false
34+
isValid: false

0 commit comments

Comments
 (0)