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;
using System;
using Aven;
using Aven.HTML;
class Index : Page
class Index : Template
{
public override void LoadPageContents()
public this()
{
this.AddElement(
Div("Content aisjdoasjiodashuihnsduijhnuisdhfsd",
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;")
);
Title = "An aven website";
OutputFile = "\\Index.html";
}
public this() : base()
public override void Body(HTML body)
{
Title = scope $"Aven - {Aven.Version}";
this.AddInlineStyle("""
body {
margin:0px;
}
""");
body.Children.Add(Div(
H1("Aven Website"),
Div("adssadsadsadsadsad", "asidosadsadsd")..SetValue("Style", "color:red;")
));
}
}

View file

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

View file

@ -1,94 +1,88 @@
namespace Aven;
using Aven.Internal;
using System;
using System.IO;
using System.Diagnostics;
using System.Collections;
class Aven
{
private static readonly Logger _defaultLogger = new .() ~ delete _;
private static readonly Builder _defaultBuilder = new .() ~ delete _;
private List<Template> _templates = new .() ~ DeleteContainerAndItems!(_);
private Stopwatch _timer = new .() ~ delete _;
public static readonly String Version = "0.5.0";
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)
///Registers an instance of a web template
public Result<void> Register(Owning<Template> template)
{
if(!Directory.Exists(dir))
return .Err;
_dirs.Add(new .(dir), new .(outdir));
return .Ok;
}
///Copy a file to the output directory
public Result<void> IncludeFileInOutput(StringView dir, StringView outdir)
{
if(!File.Exists(dir))
return .Err;
_files.Add(new .(dir), new .(outdir));
return .Ok;
}
///Builds the website
public void Build()
{
Log.Info(scope $"Starting Aven {Version}");
Log.Info("Start building");
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 .();
if (Path.GetDirectoryPath(scope $"{OutputDirectory}/{file}", outdir) case .Ok)
if (template case .Owning(let val))
{
if (Directory.CreateDirectory(outdir) case .Ok)
{
if(File.WriteAllText(scope $"{OutputDirectory}/{file}", data) case .Ok)
{
Log.Info(scope $"Wrote file to {OutputDirectory}/{file}");
}
}
Log.Debug("Registered new template instance");
_templates.Add(val);
return .Ok;
}
Log.Error(scope $"Attempt to write to {OutputDirectory}/{file} failed");
Log.Warn("Unable to register template instance");
return .Err;
}
private bool EnsureOutputExists()
public void Build(StringView outputDir = "./output")
{
if(OutputDirectory == null)
_timer.Start();
String toWrite = new .(10000);
defer delete toWrite;
Log.Info("Start building");
for (var page in _templates)
{
Log.Error("No output directory is has been set");
return false;
toWrite.Clear();
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)
{
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)
{
Log.Error(scope $"Unable to write to file: {outputDir}{page.OutputFile}");
continue;
}
Log.Info(scope $"Wrote to file: {outputDir}{page.OutputFile}");
}
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;
Log.Info(scope $"Finished generation in: {_timer.ElapsedMilliseconds}ms");
_timer.Reset();
_timer.Stop();
}
}

View file

@ -1,73 +1,73 @@
namespace Aven.HTML;
namespace Aven;
using System;
using System.Collections;
class HTML
{
static public implicit operator HTML (StringView v) => Raw(v);
private String _Name = new .("") ~ delete _;
private String _Body = new .("") ~ delete _;
private String _Id = new .("") ~ delete _;
private HashSet<String> _Classes = new .() ~ DeleteContainerAndItems!(_);
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
static public implicit operator HTML(StringView v) => Raw(v);
public List<HTML> Children = new .() ~ DeleteContainerAndItems!(_);
public Dictionary<String, String> Values = new .() ~ DeleteDictionaryAndKeysAndValues!(_);
public HashSet<String> Classes = new .() ~ DeleteContainerAndItems!(_);
public bool SelfClosing = false; //<name />
private String _name = new .("") ~ delete _;
public StringView Name
{
get => _Name;
get => _name;
set
{
_Name.Clear();
_Name.Append(value);
_name.Clear();
_name.Append(value);
}
}
private String _body = new .("") ~ delete _;
public StringView Body
{
get => _Body;
get => _body;
set
{
_Body.Clear();
_Body.Append(value);
_body.Clear();
_body.Append(value);
}
}
private String _id = new .("") ~ delete _;
public StringView Id
{
get => _Id;
get => _id;
set
{
_Id.Clear();
_Id.Append(value);
_id.Clear();
_id.Append(value);
}
}
public this(params Span<HTML> children)
{
for (var i in children)
_Children.Add(i);
Children.Add(i);
}
public void SetValue(StringView key, StringView value)
{
if (_Values.ContainsKeyAlt<StringView>(key))
_Values[scope .(key)]..Clear().Append(value);
if (Values.ContainsKeyAlt<StringView>(key))
Values[scope .(key)]..Clear().Append(value);
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 .())} />");
return;
@ -77,11 +77,11 @@ class HTML
if (Name != "")
buffer.Append(scope $"<{Name} {CalculateValues(.. scope .())}>\n");
if (_Body != .Empty)
buffer.Append(_Body);
if (_body != .Empty)
buffer.Append(_body);
for (var i in _Children)
i.Build(buffer);
for (var i in Children)
i.ToString(buffer);
if (Name != "")
buffer.Append(scope $"</{Name}>\n");
@ -89,21 +89,19 @@ class HTML
private void CalculateValues(String buffer)
{
if(_Id != .Empty)
buffer.Append(scope $"id=\"{_Id}\"");
if(_id != .Empty)
buffer.Append(scope $"id=\"{_id}\"");
if(_Classes.Count > 0)
if(Classes.Count > 0)
{
buffer.Append("class=\"");
for(var i in _Classes)
for(var i in Classes)
buffer.Append(scope $"{i} ");
buffer.Append("\" ");
}
for(var i in _Values)
{
for(var i in Values)
buffer.Append(scope $"{i.key}=\"{i.value}\"");
}
}
}
@ -112,7 +110,7 @@ static
public static HTML Raw(StringView body = "")
{
var toReturn = new HTML();
toReturn.[Friend]_Body.Append(body);
toReturn.Body = body;
return toReturn;
}
@ -137,9 +135,9 @@ static
var sum = new HTML(children: summary);
sum.Name = .("summary");
toReturn.[Friend]_Children.Add(sum);
toReturn.Children.Add(sum);
for(var i in details)
toReturn.[Friend]_Children.Add(i);
toReturn.Children.Add(i);
return toReturn;
}
@ -309,7 +307,7 @@ static
public static HTML Hr()
{
var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true;
toReturn.SelfClosing = true;
toReturn.Name = .("hr");
return toReturn;
}
@ -394,7 +392,7 @@ static
public static HTML Br()
{
var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true;
toReturn.SelfClosing = true;
toReturn.Name = .("br");
return toReturn;
}
@ -556,7 +554,7 @@ static
public static HTML Wbr()
{
var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true;
toReturn.SelfClosing = true;
toReturn.Name = .("wbr");
return toReturn;
}
@ -564,7 +562,7 @@ static
public static HTML Area()
{
var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true;
toReturn.SelfClosing = true;
toReturn.Name = .("area");
return toReturn;
}
@ -579,7 +577,7 @@ static
public static HTML Img(StringView src = "", int64 width = -1, int64 height = -1, StringView alt = "")
{
var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true;
toReturn.SelfClosing = true;
if(src != "") toReturn.SetValue("src", src);
if(alt != "") toReturn.SetValue("alt", alt);
if(width > 0) toReturn.SetValue("width", width.ToString(.. scope .()));
@ -598,7 +596,7 @@ static
public static HTML Track()
{
var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true;
toReturn.SelfClosing = true;
toReturn.Name = .("track");
return toReturn;
}
@ -613,7 +611,7 @@ static
public static HTML Embed()
{
var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true;
toReturn.SelfClosing = true;
toReturn.Name = .("embed");
return toReturn;
}
@ -656,7 +654,7 @@ static
public static HTML Source()
{
var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true;
toReturn.SelfClosing = true;
toReturn.Name = .("source");
return toReturn;
}
@ -720,7 +718,7 @@ static
public static HTML Col()
{
var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true;
toReturn.SelfClosing = true;
toReturn.Name = .("col");
return toReturn;
}
@ -812,7 +810,7 @@ static
public static HTML Input()
{
var toReturn = new HTML();
toReturn.[Friend]_SelfClosing = true;
toReturn.SelfClosing = true;
toReturn.Name = .("input");
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
}