1 module libwasm.spa;
2 
3 version (LDC) import ldc.attributes;
4 public import libwasm.types;
5 public import libwasm.dom;
6 public import libwasm.node;
7 public import libwasm.event;
8 public import libwasm.array;
9 public import libwasm.css;
10 public import libwasm.router;
11 
12 nothrow:
13 @safe void log_info(string msg)
14 {
15   import libwasm.bindings.Console;
16 
17   console.info(msg);
18 }
19 
20 @safe void log_error(string msg)
21 {
22   import libwasm.bindings.Console;
23 
24   console.error(msg);
25 }
26 
27 char[] parse_int(long num) nothrow @trusted
28 {
29   import libwasm.rt.allocator : ThreadMemAllocator;
30 
31   // used only for debugging memutils (for now)
32   import fast.format;
33 
34   char[] buf = cast(char[]) ThreadMemAllocator.allocate(decCharsVal(num));
35   return formattedWrite!"%d"(buf.ptr, num);
36 }
37 
38 extern (C)
39 {
40   Handle getRoot();
41 }
42 
43 version (unittest)
44 {
45   auto assumeNoThrow(T)(lazy T block)
46   {
47     try
48     {
49       return block();
50     }
51     catch (Exception e)
52     {
53       assert(false, e.msg);
54     }
55   }
56 
57   auto renderToNode(T)(auto ref T t) @trusted
58   {
59     import unit_threaded;
60 
61     Handle rootIdx = cast(Handle) unittest_dom_nodes.data.length;
62     auto rootNode = new UnittestDomNode(NodeType.root, rootIdx + 1);
63     unittest_dom_nodes.put(rootNode);
64     t.compile();
65     libwasm.dom.render(JsHandle(rootIdx + 1), t);
66     assert(t.getNamedNode().node != invalidHandle);
67     assert(t.getNamedNode().mounted == true);
68     return rootNode;
69   }
70 
71   string renderToString(T)(auto ref T t)
72   {
73     import std.format : format;
74 
75     static if (is(T : UnittestDomNode))
76     {
77       auto node = t;
78     }
79     else
80       auto node = t.renderToNode;
81     return format("%s", node).assumeNoThrow;
82   }
83 
84   string renderToString(T)()
85   {
86     T t;
87     return t.renderToString();
88   }
89 }
90 
91 void addApplicationCss(Application, Theme)()
92 {
93   enum css = GetCss!(Application, Theme);
94   static if (css.length > 0)
95     addCss(css);
96 }
97 
98 mixin template Spa(Application)
99 {
100   struct Empty
101   {
102   }
103 
104   mixin Spa!(Application, Empty);
105 }
106 
107 mixin template Spa(Application, Theme)
108 {
109   __gshared Application application;
110   pragma(mangle, "_start")
111   extern (C)
112   export
113   @trusted void _start(uint heap_base)
114   {
115     import libwasm.rt.memory;
116     import fast.internal.helpers : logInfo, logError;
117     import memutils.constants : writeln, parseInt;
118     import memutils.scoped;
119     alloc_init(heap_base);
120     PoolStack.initialize();
121     auto root = getRoot();
122     addApplicationCss!(Application, Theme)();
123     writeln = &log_info;
124     parseInt = &parse_int;
125     logInfo = &log_info;
126     logError = &log_error;
127     static if (__traits(hasMember, Application, "main"))
128     {
129       application.main();
130     }
131     application.compile();
132     setupRouter();
133     application.registerRoutes();
134     libwasm.dom.render(root, application);
135     static if (__traits(hasMember, Application, "ready"))
136     {
137       application.ready();
138     }
139     else router().navigateTo(document().location().front.pathname());
140   }
141 
142   version (hmr)
143   {
144     pragma(msg, "libwasm with HMR");
145     import libwasm.hmr;
146 
147     pragma(mangle, "dumpApp")
148     extern (C) export string dumpApp()
149     {
150       return application.dumpState();
151     }
152 
153     pragma(mangle, "loadApp")
154     extern (C) export void loadApp(string state)
155     {
156       application.loadState(state);
157     }
158   }
159 }
160 
161 struct Parameters(Ps...)
162 {
163   static template opDispatch(string name)
164   {
165     alias opDispatch(alias P) = Parameters!(Ps, Param!(name, P)); // does this use only public fields?
166   }
167 }
168 
169 auto param()
170 {
171   return Parameters!()();
172 }
173 
174 struct Param(string name, alias field)
175 {
176   alias Name = name;
177   alias Field = field;
178 }