Rewrite to improve performance and the overall api

This commit is contained in:
Booklordofthedings 2024-12-06 14:56:47 +01:00
parent 1fbb94e9a2
commit 9f9cff55fa
12 changed files with 237 additions and 323 deletions

View file

@ -1,34 +1,20 @@
namespace Example_Website; namespace Example_Website;
using System;
using Aven; using Aven;
using Aven.HTML;
class Index : Page class Index : Template
{ {
public this()
public override void LoadPageContents()
{ {
this.AddElement( Title = "An aven website";
Div("Content aisjdoasjiodashuihnsduijhnuisdhfsd", OutputFile = "\\Index.html";
H1("Hello from Aven"),
Raw("Aven is a website generation library, that can be used from"), A("Beef")..SetValue("href", "https://Beeflang.org"), Raw("to generate reuseable html."), Br(),
Raw("By being integrated into a programming language you can freely write your own templating and generation logic"), Br()
)..SetValue("style", "padding: 15px;")
);
} }
public this() : base() public override void Body(HTML body)
{ {
Title = scope $"Aven - {Aven.Version}"; body.Children.Add(Div(
H1("Aven Website"),
this.AddInlineStyle(""" Div("adssadsadsadsadsad", "asidosadsadsd")..SetValue("Style", "color:red;")
body { ));
margin:0px;
}
""");
} }
} }

View file

@ -9,8 +9,7 @@ class Program
public static void Main() public static void Main()
{ {
Aven aven = scope .(); Aven aven = scope .();
aven.Pages.Add(scope Example_Website.Index()); aven.Register(.Owning(new Example_Website.Index()));
aven.OutputDirectory = "../output/";
aven.Build(); aven.Build();
} }
} }

View file

@ -1,94 +1,88 @@
namespace Aven; namespace Aven;
using Aven.Internal;
using System; using System;
using System.IO; using System.IO;
using System.Diagnostics;
using System.Collections; using System.Collections;
class Aven class Aven
{ {
private static readonly Logger _defaultLogger = new .() ~ delete _; private List<Template> _templates = new .() ~ DeleteContainerAndItems!(_);
private static readonly Builder _defaultBuilder = new .() ~ delete _; private Stopwatch _timer = new .() ~ delete _;
public static readonly String Version = "0.5.0"; ///Registers an instance of a web template
public Result<void> Register(Owning<Template> template)
private Dictionary<String, String> _dirs = new .() ~ DeleteDictionaryAndKeysAndValues!(_);
private Dictionary<String, String> _files = new .() ~ DeleteDictionaryAndKeysAndValues!(_);
public List<Page> Pages = new .(10) ~ delete _;
public Logger Log = _defaultLogger;
public Builder Builder = _defaultBuilder;
public String OutputDirectory = null;
///Copy a directory to the output directory
public Result<void> IncludeDirInOutput(StringView dir, StringView outdir)
{ {
if(!Directory.Exists(dir)) if (template case .Owning(let val))
return .Err; {
_dirs.Add(new .(dir), new .(outdir)); Log.Debug("Registered new template instance");
_templates.Add(val);
return .Ok; return .Ok;
} }
Log.Warn("Unable to register template instance");
///Copy a file to the output directory
public Result<void> IncludeFileInOutput(StringView dir, StringView outdir)
{
if(!File.Exists(dir))
return .Err; return .Err;
_files.Add(new .(dir), new .(outdir));
return .Ok;
} }
///Builds the website public void Build(StringView outputDir = "./output")
public void Build()
{ {
Log.Info(scope $"Starting Aven {Version}"); _timer.Start();
String toWrite = new .(10000);
defer delete toWrite;
Log.Info("Start building"); Log.Info("Start building");
for (var page in _templates)
if(!EnsureOutputExists())
return;
Builder.Build(this);
Log.Info("Successfully build");
}
///Writes a file to the location specified by output
private Result<void> WriteToOutput(StringView file, StringView data)
{ {
String outdir = scope .(); toWrite.Clear();
if (Path.GetDirectoryPath(scope $"{OutputDirectory}/{file}", outdir) case .Ok) toWrite
..Append("""
<!doctype html>
<html>
<head>
""")
..Append("<title>")
..Append(page.Title)
..Append("</title>\n")
..Append("<script>")
..Append(page.Scripts)
..Append("</script>\n")
..Append("<style>")
..Append(page.Styles)
..Append("</style>\n")
..Append("</head>\n");
var body = page.Body(.. Custom("body"))..ToString(toWrite);
delete body;
toWrite.Append("</html>\n");
Log.Debug("Successfully build page");
String outFile = scope $"{outputDir}/{page.OutputFile}";
String outDir = scope .();
if (Path.GetDirectoryPath(outFile, outDir) case .Err)
{ {
if (Directory.CreateDirectory(outdir) case .Ok) Log.Error(scope $"Unable to write to file: {outputDir}{page.OutputFile}");
continue;
}
Directory.CreateDirectory(outDir).IgnoreError();
if (File.WriteAllText(scope $"{outputDir}{page.OutputFile}", toWrite) case .Err)
{ {
if(File.WriteAllText(scope $"{OutputDirectory}/{file}", data) case .Ok) Log.Error(scope $"Unable to write to file: {outputDir}{page.OutputFile}");
{ continue;
Log.Info(scope $"Wrote file to {OutputDirectory}/{file}");
} }
} Log.Info(scope $"Wrote to file: {outputDir}{page.OutputFile}");
}
Log.Error(scope $"Attempt to write to {OutputDirectory}/{file} failed");
return .Err;
} }
private bool EnsureOutputExists() Log.Info(scope $"Finished generation in: {_timer.ElapsedMilliseconds}ms");
{ _timer.Reset();
if(OutputDirectory == null) _timer.Stop();
{
Log.Error("No output directory is has been set");
return false;
}
if(Directory.Exists(OutputDirectory))
return true;
if(Directory.CreateDirectory(OutputDirectory) case .Err)
{
Log.Error("Unable to create output directoy");
return false;
}
Log.Info("Output directory has been created");
return true;
} }
} }

View file

@ -1,4 +1,4 @@
namespace Aven.HTML; namespace Aven;
using System; using System;
using System.Collections; using System.Collections;
@ -7,67 +7,67 @@ class HTML
{ {
static public implicit operator HTML(StringView v) => Raw(v); static public implicit operator HTML(StringView v) => Raw(v);
private String _Name = new .("") ~ delete _; public List<HTML> Children = new .() ~ DeleteContainerAndItems!(_);
private String _Body = new .("") ~ delete _; public Dictionary<String, String> Values = new .() ~ DeleteDictionaryAndKeysAndValues!(_);
private String _Id = new .("") ~ delete _; public HashSet<String> Classes = new .() ~ DeleteContainerAndItems!(_);
private HashSet<String> _Classes = new .() ~ DeleteContainerAndItems!(_); public bool SelfClosing = false; //<name />
private List<HTML> _Children = new .() ~ DeleteContainerAndItems!(_);
private bool _SelfClosing = false; //makes an object <name />
private Dictionary<String, String> _Values = new .() ~ DeleteDictionaryAndKeysAndValues!(_); //Most of these values should come from fields
//This is just used to allow arbitrary values for stuff ive missed
private String _name = new .("") ~ delete _;
public StringView Name public StringView Name
{ {
get => _Name; get => _name;
set set
{ {
_Name.Clear(); _name.Clear();
_Name.Append(value); _name.Append(value);
} }
} }
private String _body = new .("") ~ delete _;
public StringView Body public StringView Body
{ {
get => _Body; get => _body;
set set
{ {
_Body.Clear(); _body.Clear();
_Body.Append(value); _body.Append(value);
} }
} }
private String _id = new .("") ~ delete _;
public StringView Id public StringView Id
{ {
get => _Id; get => _id;
set set
{ {
_Id.Clear(); _id.Clear();
_Id.Append(value); _id.Append(value);
} }
} }
public this(params Span<HTML> children) public this(params Span<HTML> children)
{ {
for (var i in children) for (var i in children)
_Children.Add(i); Children.Add(i);
} }
public void SetValue(StringView key, StringView value) public void SetValue(StringView key, StringView value)
{ {
if (_Values.ContainsKeyAlt<StringView>(key)) if (Values.ContainsKeyAlt<StringView>(key))
_Values[scope .(key)]..Clear().Append(value); Values[scope .(key)]..Clear().Append(value);
else else
_Values.Add(new .(key), new .(value)); Values.Add(new .(key), new .(value));
} }
public void AddClass(StringView clas) => _Classes.Add(new .(clas)); public void SetBody(StringView value) => Body = value;
public void SetId(StringView value) => Id = value;
public void AddClass(StringView clas) => Classes.Add(new .(clas));
public void RemoveClass(StringView clas) => Classes.Remove(scope .(clas));
public void RemoveClass(StringView clas) => _Classes.Remove(scope .(clas));
public void Build(String buffer) public override void ToString(String buffer)
{ {
if (_SelfClosing && _Name != "") if (SelfClosing && Name != "")
{ {
buffer.Append(scope $"<{Name} {CalculateValues(.. scope .())} />"); buffer.Append(scope $"<{Name} {CalculateValues(.. scope .())} />");
return; return;
@ -77,11 +77,11 @@ class HTML
if (Name != "") if (Name != "")
buffer.Append(scope $"<{Name} {CalculateValues(.. scope .())}>\n"); buffer.Append(scope $"<{Name} {CalculateValues(.. scope .())}>\n");
if (_Body != .Empty) if (_body != .Empty)
buffer.Append(_Body); buffer.Append(_body);
for (var i in _Children) for (var i in Children)
i.Build(buffer); i.ToString(buffer);
if (Name != "") if (Name != "")
buffer.Append(scope $"</{Name}>\n"); buffer.Append(scope $"</{Name}>\n");
@ -89,30 +89,28 @@ class HTML
private void CalculateValues(String buffer) private void CalculateValues(String buffer)
{ {
if(_Id != .Empty) if(_id != .Empty)
buffer.Append(scope $"id=\"{_Id}\""); buffer.Append(scope $"id=\"{_id}\"");
if(_Classes.Count > 0) if(Classes.Count > 0)
{ {
buffer.Append("class=\""); buffer.Append("class=\"");
for(var i in _Classes) for(var i in Classes)
buffer.Append(scope $"{i} "); buffer.Append(scope $"{i} ");
buffer.Append("\" "); buffer.Append("\" ");
} }
for(var i in _Values) for(var i in Values)
{
buffer.Append(scope $"{i.key}=\"{i.value}\""); buffer.Append(scope $"{i.key}=\"{i.value}\"");
} }
} }
}
static static
{ {
public static HTML Raw(StringView body = "") public static HTML Raw(StringView body = "")
{ {
var toReturn = new HTML(); var toReturn = new HTML();
toReturn.[Friend]_Body.Append(body); toReturn.Body = body;
return toReturn; return toReturn;
} }
@ -137,9 +135,9 @@ static
var sum = new HTML(children: summary); var sum = new HTML(children: summary);
sum.Name = .("summary"); sum.Name = .("summary");
toReturn.[Friend]_Children.Add(sum); toReturn.Children.Add(sum);
for(var i in details) for(var i in details)
toReturn.[Friend]_Children.Add(i); toReturn.Children.Add(i);
return toReturn; return toReturn;
} }
@ -309,7 +307,7 @@ static
public static HTML Hr() public static HTML Hr()
{ {
var toReturn = new HTML(); var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true; toReturn.SelfClosing = true;
toReturn.Name = .("hr"); toReturn.Name = .("hr");
return toReturn; return toReturn;
} }
@ -394,7 +392,7 @@ static
public static HTML Br() public static HTML Br()
{ {
var toReturn = new HTML(); var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true; toReturn.SelfClosing = true;
toReturn.Name = .("br"); toReturn.Name = .("br");
return toReturn; return toReturn;
} }
@ -556,7 +554,7 @@ static
public static HTML Wbr() public static HTML Wbr()
{ {
var toReturn = new HTML(); var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true; toReturn.SelfClosing = true;
toReturn.Name = .("wbr"); toReturn.Name = .("wbr");
return toReturn; return toReturn;
} }
@ -564,7 +562,7 @@ static
public static HTML Area() public static HTML Area()
{ {
var toReturn = new HTML(); var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true; toReturn.SelfClosing = true;
toReturn.Name = .("area"); toReturn.Name = .("area");
return toReturn; return toReturn;
} }
@ -579,7 +577,7 @@ static
public static HTML Img(StringView src = "", int64 width = -1, int64 height = -1, StringView alt = "") public static HTML Img(StringView src = "", int64 width = -1, int64 height = -1, StringView alt = "")
{ {
var toReturn = new HTML(); var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true; toReturn.SelfClosing = true;
if(src != "") toReturn.SetValue("src", src); if(src != "") toReturn.SetValue("src", src);
if(alt != "") toReturn.SetValue("alt", alt); if(alt != "") toReturn.SetValue("alt", alt);
if(width > 0) toReturn.SetValue("width", width.ToString(.. scope .())); if(width > 0) toReturn.SetValue("width", width.ToString(.. scope .()));
@ -598,7 +596,7 @@ static
public static HTML Track() public static HTML Track()
{ {
var toReturn = new HTML(); var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true; toReturn.SelfClosing = true;
toReturn.Name = .("track"); toReturn.Name = .("track");
return toReturn; return toReturn;
} }
@ -613,7 +611,7 @@ static
public static HTML Embed() public static HTML Embed()
{ {
var toReturn = new HTML(); var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true; toReturn.SelfClosing = true;
toReturn.Name = .("embed"); toReturn.Name = .("embed");
return toReturn; return toReturn;
} }
@ -656,7 +654,7 @@ static
public static HTML Source() public static HTML Source()
{ {
var toReturn = new HTML(); var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true; toReturn.SelfClosing = true;
toReturn.Name = .("source"); toReturn.Name = .("source");
return toReturn; return toReturn;
} }
@ -720,7 +718,7 @@ static
public static HTML Col() public static HTML Col()
{ {
var toReturn = new HTML(); var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true; toReturn.SelfClosing = true;
toReturn.Name = .("col"); toReturn.Name = .("col");
return toReturn; return toReturn;
} }
@ -812,7 +810,7 @@ static
public static HTML Input() public static HTML Input()
{ {
var toReturn = new HTML(); var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true; toReturn.SelfClosing = true;
toReturn.Name = .("input"); toReturn.Name = .("input");
return toReturn; return toReturn;
} }

View file

@ -1,23 +0,0 @@
namespace Aven;
using System;
using System.IO;
using System.Collections;
class Builder
{
public void Build(Aven aven)
{
aven.Log.Debug("Default Builder");
String output = new .(1000);
defer delete output;
for(var page in aven.Pages)
{
page.Build(output);
if(aven.[Friend]WriteToOutput(page.GetPageFileOutput(), output) case .Err)
aven.Log.Warn("Page has not been written");
}
}
}

35
src/Internal/Log.bf Normal file
View file

@ -0,0 +1,35 @@
namespace Aven.Internal;
using System;
class Log
{
#if DEBUG
public static LogLevel LogLevel = .Debug;
#else
public static LogLevel LogLevel = .Info;
#endif
public static void Log(LogLevel level, StringView message)
{
if(level.Underlying >= LogLevel.Underlying)
switch(level)
{
case .Debug:
Console.WriteLine(scope $"\x1b[36m[Debug]:{message}\x1b[39m");
case .Info:
Console.WriteLine(scope $"\x1b[34m[Info]:{message}\x1b[39m");
case .Warning:
Console.WriteLine(scope $"\x1b[33m[Warn]:{message}\x1b[39m");
case .Error:
Console.WriteLine(scope $"\x1b[31m[Err]:{message}\x1b[39m");
}
}
public static void Debug(StringView message) => Log(.Debug, message);
public static void Info(StringView message) => Log(.Info, message);
public static void Warn(StringView message) => Log(.Warning, message);
public static void Error(StringView message) => Log(.Error, message);
}

9
src/Internal/LogLevel.bf Normal file
View file

@ -0,0 +1,9 @@
namespace Aven.Internal;
enum LogLevel
{
Debug,
Info,
Warning,
Error
}

View file

@ -1,41 +0,0 @@
namespace Aven;
using System;
class Logger
{
public enum LogLevel
{
Debug,
Info,
Warn,
Error
}
#if DEBUG
public LogLevel CurrentLogLevel = .Debug;
#else
public LogLevel CurrentLogLevel = .Info;
#endif
///Log a message
public virtual void Log(LogLevel logLevel, StringView message)
{
switch(logLevel)
{
case .Debug:
Console.WriteLine(scope $"[Aven][Debug]: {message}");
case .Info:
Console.WriteLine(scope $"[Aven][Info]: {message}");
case .Warn:
Console.WriteLine(scope $"[Aven][Warn]: {message}");
case .Error:
Console.WriteLine(scope $"[Aven][Error]: {message}");
}
}
public void Debug(StringView message) => Log(.Debug, message);
public void Info(StringView message) => Log(.Info, message);
public void Warn(StringView message) => Log(.Warn, message);
public void Error(StringView message) => Log(.Error, message);
}

12
src/Internal/Owning.bf Normal file
View file

@ -0,0 +1,12 @@
namespace Aven.Internal;
/*
Owning indicates object ownership.
A method which has this enum as a parameter takes
over ownership of the object and ensures proper deletion
*/
enum Owning<T>
{
case Owning(T val);
}

View file

@ -1,73 +0,0 @@
namespace Aven;
using Aven.HTML;
using System;
using System.Collections;
/**
An instance of this class represents a single .html website
*/
abstract class Page
{
private String _title = new .("") ~ delete _;
private List<HTML> _elements = new .() ~ DeleteContainerAndItems!(_);
private List<String> _scripts = new .() ~ DeleteContainerAndItems!(_);
private List<String> _inlineStyle = new .() ~ DeleteContainerAndItems!(_);
private List<String> _styles = new .() ~ DeleteContainerAndItems!(_);
public virtual StringView GetPageFileOutput() => "Index.html";
public void AddElement(params Span<HTML> elmt)
{
for(var i in elmt)
_elements.Add(i);
}
public void Build(String buffer)
{
LoadPageContents();
buffer.Append(scope $"""
<!Doctype HTML>
<!-- Made with Quill version 0.1 -->
<html>
<head>
<title>{Title}</title>
{BuildStyles(.. scope .())}
{BuildScripts(.. scope .())}
</head>
<body>
{BuildElements(.. scope .())}
</body>
</html>
""");
}
private void BuildElements(String buffer)
{
for(var i in _elements)
{
i.Build(buffer);
buffer.Append("\n");
}
}
private void BuildStyles(String buffer)
{
for(var i in _styles)
buffer.Append(scope $"<link rel=\"stylesheet\" href=\"{i}\" />\n");
for(var i in _inlineStyle)
buffer.Append(scope $"""
<style>
{i}
</style>
""");
}
private void BuildScripts(String buffer)
{
for(var i in _scripts)
buffer.Append(scope $"<script src=\"{i}\"></script>\n");
}
}

View file

@ -1,36 +0,0 @@
namespace Aven;
using System;
extension Page
{
public StringView Title
{
get => _title;
set
{
_title.Clear();
_title.Append(value);
}
}
public void AddScripts(params Span<StringView> scripts)
{
for(var i in scripts)
_scripts.Add(new .(i));
}
public void AddStyles(params Span<StringView> styles)
{
for(var i in styles)
_styles.Add(new .(i));
}
public void AddInlineStyle(StringView style)
{
_inlineStyle.Add(new .(style));
}
public abstract void LoadPageContents();
}

54
src/Template.bf Normal file
View file

@ -0,0 +1,54 @@
namespace Aven;
using System;
abstract class Template
{
private String _output = new .("Index.html") ~ delete _;
public StringView OutputFile
{
get => _output;
set
{
_output.Clear();
_output.Append(value);
}
}
private String _title = new .() ~ delete _;
public StringView Title
{
get => _title;
set
{
_title.Clear();
_title.Append(value);
}
}
private String _scripts = new .() ~ delete _;
public StringView Scripts
{
get => _scripts;
set
{
_scripts.Clear();
_scripts.Append(value);
}
}
private String _styles = new .() ~ delete _;
public StringView Styles
{
get => _styles;
set
{
_styles.Clear();
_styles.Append(value);
}
}
public abstract void Body(HTML body); ///Returns the html element thats supposed to be the page body
public virtual void Head(String buffer) {}; //Allows template files to have their own custom head beyond our defaults
}