-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
1915 lines (1879 loc) · 304 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
<title></title>
<link rel="self" type="application/atom+xml" href="https://spacedimp.com/atom.xml"/>
<link rel="alternate" type="text/html" href="https://spacedimp.com"/>
<generator uri="https://www.getzola.org/">Zola</generator>
<updated>2024-11-29T00:00:00+00:00</updated>
<id>https://spacedimp.com/atom.xml</id>
<entry xml:lang="en">
<title>Getting Started with SurrealDB 2 and Axum for Web Development</title>
<published>2024-11-29T00:00:00+00:00</published>
<updated>2024-11-29T00:00:00+00:00</updated>
<author>
<name>
Unknown
</name>
</author>
<link rel="alternate" type="text/html" href="https://spacedimp.com/blog/using-rust-axum-surrealdb-to-build-a-webapp/"/>
<id>https://spacedimp.com/blog/using-rust-axum-surrealdb-to-build-a-webapp/</id>
<content type="html" xml:base="https://spacedimp.com/blog/using-rust-axum-surrealdb-to-build-a-webapp/"><p><img src="/img/gif/surreal.gif" alt="Alt text" title="demo of Rust, Axum, SurrealDB" /></p>
<p>The goal of this blog post is to help beginners grasp the basics of using SurrealDB 2 and Axum for web development.</p>
<blockquote>
<p>I find the technology fascinating, but readers should be aware that SurrealDB v1 upset many users due to the lack of documentation and poor performance</p>
</blockquote>
<p>However, version 2 fixes several performance and bug issues. One such improvement is that the in-memory storage is now powered by SurrealKV, which is concurrent, meaning that read/write operations scale with the number of CPUs.</p>
<h2 id="understanding-surrealdb-s-architecture">Understanding SurrealDB's Architecture</h2>
<p>SurrealDB separates its query language (called <a rel="noopener" target="_blank" href="https://surrealdb.com/docs/surrealql">SurrealQL</a>) from its data storage backend. This flexibility allows you to choose the best storage option for your project. While I'll be using SurrealKV locally for simplicity, <a rel="noopener" target="_blank" href="https://docs.rs/surrealdb/latest/surrealdb/engine/any/fn.connect.html#examples">other options</a> are available, such as TikV for distributed storage or other in-memory solutions.</p>
<p><a rel="noopener" target="_blank" href="https://surrealdb.com/docs/surrealql">SurrealQL</a> from the data storage. I'll be using SurrealKV locally but there are other options including TikV for distributed and in memory to name a <a rel="noopener" target="_blank" href="https://docs.rs/surrealdb/latest/surrealdb/engine/any/fn.connect.html#examples">few</a>.</p>
<p>Check out the SurrealDB <a rel="noopener" target="_blank" href="https://surrealdb.com/docs/surrealdb/introduction/start">getting started</a> page to start up an in memory data store. I would recommend clicking on "Using CLI" to learn how to connect to the database in the terminal.</p>
<p>I found the <a rel="noopener" target="_blank" href="https://surrealdb.com/learn/book">book</a> very helpful in learning the language.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>Before getting started, make sure you have the following installed:</p>
<ul>
<li>
<p><a rel="noopener" target="_blank" href="https://www.rust-lang.org/tools/install">Rust</a> installed</p>
</li>
<li>
<p><a rel="noopener" target="_blank" href="https://github.com/surrealdb/surrealdb?tab=readme-ov-file#installation">SurrealDB</a> installed</p>
</li>
</ul>
<h2 id="step-1-create-the-project">Step 1. Create the Project</h2>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">cargo</span><span> new webapp
</span></code></pre>
<p>add dependencies</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#56b6c2;">cd</span><span> webapp
</span><span>
</span><span style="color:#e06c75;">cargo</span><span> add axum
</span><span style="color:#e06c75;">cargo</span><span> add serde</span><span style="color:#e06c75;"> --features</span><span> derive
</span><span style="color:#e06c75;">cargo</span><span> add surrealdb</span><span style="color:#e06c75;"> --features</span><span> kv-surrealkv
</span><span style="color:#e06c75;">cargo</span><span> add thiserror
</span><span style="color:#e06c75;">cargo</span><span> add tokio</span><span style="color:#e06c75;"> --features</span><span> full
</span><span style="color:#e06c75;">cargo</span><span> add serde_json
</span></code></pre>
<br>
<p>Now start up a SurrealKV storage engine at localhost:8080</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">surreal</span><span> start</span><span style="color:#e06c75;"> --user</span><span> root</span><span style="color:#e06c75;"> --pass</span><span> password</span><span style="color:#e06c75;"> --bind</span><span> 127.0.0.1:8080 surrealkv://mydb
</span></code></pre>
<p>In depth examples of using surreal start <a rel="noopener" target="_blank" href="https://surrealdb.com/docs/surrealdb/cli/start">here</a>. Make sure to select V2.x</p>
<p>From a second terminal, connect to the engine and set the database to test_db and namespace to test_ns</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">surreal</span><span> sql</span><span style="color:#e06c75;"> --endpoint</span><span> http://127.0.0.1:8080</span><span style="color:#e06c75;"> --user</span><span> root</span><span style="color:#e06c75;"> --pass</span><span> password</span><span style="color:#e06c75;"> --ns</span><span> test_ns</span><span style="color:#e06c75;"> --db</span><span> test_db
</span></code></pre>
<p>In depth examples of using surreal sql <a rel="noopener" target="_blank" href="https://surrealdb.com/docs/surrealdb/cli/sql">here</a></p>
<h2 id="step-2-insert-some-data-in-the-database">Step 2. Insert some data in the database</h2>
<p>After the last surreal sql ... statement you should be connected:</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">test_ns/test_db</span><span>&gt;
</span></code></pre>
<p>I will create a <a rel="noopener" target="_blank" href="https://surrealdb.com/docs/surrealql/statements/define/table">TABLE</a> called users and set the table to have structured, predictable data types with a <a rel="noopener" target="_blank" href="https://surrealdb.com/docs/tutorials/define-a-schema">Schema</a>.</p>
<pre data-lang="sql" style="background-color:#282c34;color:#abb2bf;" class="language-sql "><code class="language-sql" data-lang="sql"><span>DEFINE TABLE IF NOT EXISTS users SCHEMAFULL;
</span></code></pre>
<p>Scrolling down in that last Schema link will show the user that at any point we can add a field to our table that is schemaless using FLELXIBLE. Perhaps for metatdata.</p>
<p>Let's add a username <a rel="noopener" target="_blank" href="https://surrealdb.com/docs/surrealql/statements/define/field">FIELD</a> to the table that's only allowed to be under 14 characters long</p>
<pre data-lang="sql" style="background-color:#282c34;color:#abb2bf;" class="language-sql "><code class="language-sql" data-lang="sql"><span>DEFINE FIELD username ON TABLE users TYPE string ASSERT string::len($value) &lt; </span><span style="color:#d19a66;">14</span><span>;
</span></code></pre>
<p>Next we can define an <a rel="noopener" target="_blank" href="https://surrealdb.com/docs/surrealql/statements/define/indexes">INDEX</a> for the username to make it a unique field.</p>
<p>Reading through the docs tells us that indexes are used to speed up query execution times dramatically.</p>
<pre data-lang="sql" style="background-color:#282c34;color:#abb2bf;" class="language-sql "><code class="language-sql" data-lang="sql"><span>DEFINE INDEX usernameIndex ON TABLE users COLUMNS username UNIQUE;
</span></code></pre>
<p>Now let's <a rel="noopener" target="_blank" href="https://surrealdb.com/docs/surrealql/statements/create">CREATE</a> a user</p>
<pre data-lang="sql" style="background-color:#282c34;color:#abb2bf;" class="language-sql "><code class="language-sql" data-lang="sql"><span>CREATE users </span><span style="color:#c678dd;">SET</span><span> username = </span><span style="color:#98c379;">&quot;User1&quot;</span><span>;
</span></code></pre>
<p>and we can see from the output that SurrealDB automatically gives us an id field</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">[[{</span><span> id: users:2272f7xnqi502d9it9q4, username: </span><span style="color:#98c379;">&#39;User1&#39; </span><span>}</span><span style="color:#e06c75;">]]
</span></code></pre>
<p>we could have also created the unique id with this command (skip, just for example):</p>
<pre data-lang="sql" style="background-color:#282c34;color:#abb2bf;" class="language-sql "><code class="language-sql" data-lang="sql"><span>CREATE users:</span><span style="color:#d19a66;">1 </span><span style="color:#c678dd;">SET</span><span> username = </span><span style="color:#98c379;">&quot;User1&quot;</span><span>;
</span></code></pre>
<br>
<h2 id="step-3-write-some-rust-code">Step 3. Write some Rust code</h2>
<p>Open src/main.rs and grab all the dependencies we added</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c678dd;">use </span><span>axum::{extract::State, routing::get, Router, Json, extract::Path, response::IntoResponse };
</span><span style="color:#c678dd;">use </span><span>surrealdb::Surreal;
</span><span style="color:#c678dd;">use </span><span>serde::{Deserialize, Serialize};
</span><span style="color:#c678dd;">use </span><span>std::sync::Arc;
</span><span style="color:#c678dd;">use </span><span>tokio::sync::Mutex;
</span><span style="color:#c678dd;">use </span><span>surrealdb::engine::any::Any;
</span></code></pre>
<p>We should pause here and take a look at the official SurrealDB example of using the <a rel="noopener" target="_blank" href="https://surrealdb.com/docs/sdk/rust/frameworks/axum">Rust SDK</a></p>
<p>Ok so it says we should convert Surreal errors into Axum responses (copy paste the boilerplate)</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c678dd;">mod </span><span>error {
</span><span> </span><span style="color:#c678dd;">use </span><span>axum::http::StatusCode;
</span><span> </span><span style="color:#c678dd;">use </span><span>axum::response::IntoResponse;
</span><span> </span><span style="color:#c678dd;">use </span><span>axum::response::Response;
</span><span> </span><span style="color:#c678dd;">use </span><span>axum::Json;
</span><span> </span><span style="color:#c678dd;">use </span><span>thiserror::Error;
</span><span>
</span><span> #[</span><span style="color:#e06c75;">derive</span><span>(Error, Debug)]
</span><span> </span><span style="color:#c678dd;">pub enum </span><span>Error {
</span><span> #[</span><span style="color:#e06c75;">error</span><span>(</span><span style="color:#98c379;">&quot;database error&quot;</span><span>)]
</span><span> Db,
</span><span> }
</span><span>
</span><span> </span><span style="color:#c678dd;">impl </span><span>IntoResponse </span><span style="color:#c678dd;">for </span><span>Error {
</span><span> </span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">into_response</span><span>(</span><span style="color:#e06c75;">self</span><span>) -&gt; Response {
</span><span> </span><span style="color:#c678dd;">match </span><span style="color:#e06c75;">self </span><span>{
</span><span> Error::Db=&gt; (StatusCode::</span><span style="color:#d19a66;">INTERNAL_SERVER_ERROR</span><span>, Json(</span><span style="color:#98c379;">&quot;An error has occurred. Please try again later.&quot;</span><span>.</span><span style="color:#56b6c2;">to_string</span><span>())).</span><span style="color:#56b6c2;">into_response</span><span>(),
</span><span> }
</span><span> }
</span><span> }
</span><span>
</span><span> </span><span style="color:#c678dd;">impl </span><span>From&lt;surrealdb::Error&gt; </span><span style="color:#c678dd;">for </span><span>Error {
</span><span> </span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">from</span><span>(</span><span style="color:#e06c75;">error</span><span>: surrealdb::Error) -&gt; </span><span style="color:#c678dd;">Self </span><span>{
</span><span> eprintln!(</span><span style="color:#98c379;">&quot;</span><span style="color:#d19a66;">{error}</span><span style="color:#98c379;">&quot;</span><span>);
</span><span> </span><span style="color:#c678dd;">Self</span><span>::Db
</span><span> }
</span><span> }
</span><span>}
</span></code></pre>
<p>Alright with the boilerplate out of the way I want to discuss <a rel="noopener" target="_blank" href="https://surrealdb.com/blog/introducing-surrealany-dynamic-support-for-any-engine-in-rust">surrealdb::engine::any</a></p>
<p>I mentioned that I would be using SurrealKV as the database engine, but really SurrealDB doesn't force us to stick with one. The link right above explains how the query language is completely separate from the database engine.</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="font-style:italic;color:#5c6370;">// make sure our database is accessible from all routes
</span><span>#[</span><span style="color:#e06c75;">derive</span><span>(Clone)]
</span><span style="color:#c678dd;">struct </span><span>AppState {
</span><span> </span><span style="color:#e06c75;">db</span><span>: Arc&lt;Mutex&lt;Surreal&lt;Any&gt;&gt;&gt;,
</span><span>}
</span></code></pre>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#[</span><span style="color:#e06c75;">tokio</span><span>::</span><span style="color:#e06c75;">main</span><span>]
</span><span>async </span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">main</span><span>() -&gt; Result&lt;(), error::Error&gt;{
</span><span> </span><span style="font-style:italic;color:#5c6370;">// any allows us to swap between rocksdb://mydb, mem://, etc.
</span><span> </span><span style="color:#c678dd;">let</span><span> db = surrealdb::engine::any::connect(</span><span style="color:#98c379;">&quot;surrealkv://mydb&quot;</span><span>).await?;
</span><span>
</span><span style="font-style:italic;color:#5c6370;">// authenticate
</span><span>db.</span><span style="color:#56b6c2;">signin</span><span>(surrealdb::opt::auth::Root {
</span><span> username: </span><span style="color:#98c379;">&quot;root&quot;</span><span>,
</span><span> password: </span><span style="color:#98c379;">&quot;password&quot;</span><span>,
</span><span>}).await?;
</span><span>
</span><span style="font-style:italic;color:#5c6370;">// select a namespace and a database inside that namespace
</span><span>db.</span><span style="color:#56b6c2;">use_ns</span><span>(</span><span style="color:#98c379;">&quot;test_ns&quot;</span><span>).</span><span style="color:#56b6c2;">use_db</span><span>(</span><span style="color:#98c379;">&quot;test_db&quot;</span><span>).await?;
</span><span>
</span><span style="font-style:italic;color:#5c6370;">// for routes to access the database, Axum provides AppState
</span><span style="color:#c678dd;">let</span><span> app_state = AppState {
</span><span> db: Arc::new(Mutex::new(db))
</span><span>};
</span><span>
</span><span style="font-style:italic;color:#5c6370;">// define our routes to create, delete, get users
</span><span style="color:#c678dd;">let</span><span> app = Router::new()
</span><span> .</span><span style="color:#56b6c2;">route</span><span>(</span><span style="color:#98c379;">&quot;/&quot;</span><span>, </span><span style="color:#56b6c2;">get</span><span>(get_users))
</span><span> .</span><span style="color:#56b6c2;">route</span><span>(</span><span style="color:#98c379;">&quot;/create/:uname&quot;</span><span>, </span><span style="color:#56b6c2;">get</span><span>(create_user))
</span><span> .</span><span style="color:#56b6c2;">route</span><span>(</span><span style="color:#98c379;">&quot;/delete/:uname&quot;</span><span>, </span><span style="color:#56b6c2;">get</span><span>(delete_user))
</span><span> .</span><span style="color:#56b6c2;">with_state</span><span>(app_state);
</span><span>
</span><span style="color:#c678dd;">let</span><span> listener = tokio::net::TcpListener::bind(</span><span style="color:#98c379;">&quot;0.0.0.0:3000&quot;</span><span>).await.</span><span style="color:#56b6c2;">unwrap</span><span>();
</span><span>
</span><span>axum::serve(listener, app).await.</span><span style="color:#56b6c2;">unwrap</span><span>();
</span><span>
</span><span>Ok(())
</span><span>}
</span></code></pre>
<p>For brevity I'll just use get requests with the data directly in the path.</p>
<p>However, you can use RESTful APIs such as delete, post, etc. Then access the <a rel="noopener" target="_blank" href="https://surrealdb.com/docs/surrealdb/integration/http">endpoints</a> with curl</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">curl -I</span><span> http://localhost:8080/status
</span><span>
</span><span style="color:#e06c75;">HTTP/1.1</span><span> 200 OK
</span><span style="color:#e06c75;">content-length:</span><span> 0
</span><span style="color:#e06c75;">vary:</span><span> origin, access-control-request-method, access-control-request-headers
</span><span style="color:#e06c75;">access-control-allow-origin: </span><span>*
</span><span style="color:#e06c75;">surreal-version:</span><span> surrealdb-2.0.0+20240910.8f30ee08
</span><span style="color:#e06c75;">server:</span><span> SurrealDB
</span><span style="color:#e06c75;">x-request-id:</span><span> 3dedcc96-4d8a-451e-b60d-4eaac14fa3f8
</span><span style="color:#e06c75;">date:</span><span> Wed, 11 Sep 2024 00:52:49 GMT
</span></code></pre>
<p>Now we should write a database query to define our table if it doesn't exist. Just like we did manually at the start of the tutorial.</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span>db.</span><span style="color:#56b6c2;">query</span><span>(
</span><span> </span><span style="color:#98c379;">&quot;DEFINE TABLE IF NOT EXISTS users SCHEMAFULL;
</span><span style="color:#98c379;"> DEFINE FIELD IF NOT EXISTS username ON TABLE users TYPE string ASSERT string::len($value) &lt; 14;
</span><span style="color:#98c379;"> DEFINE INDEX IF NOT EXISTS usernameIndex ON TABLE users COLUMNS username UNIQUE;
</span><span style="color:#98c379;"> &quot;
</span><span>).await?;
</span></code></pre>
<p>And now we create the routes</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="font-style:italic;color:#5c6370;">// schema to insert or get users
</span><span>#[</span><span style="color:#e06c75;">derive</span><span>(Serialize, Deserialize, Clone)]
</span><span style="color:#c678dd;">pub struct </span><span>User {
</span><span> </span><span style="color:#e06c75;">username</span><span>: String,
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5c6370;">// retrieve all users
</span><span style="font-style:italic;color:#5c6370;">// 127.0.0.1:3000/
</span><span>async </span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">get_users</span><span>(State(</span><span style="color:#e06c75;">state</span><span>): State&lt;AppState&gt;) -&gt; Result&lt;Json&lt;Vec&lt;User&gt;&gt;, error::Error&gt;{
</span><span> </span><span style="color:#c678dd;">let</span><span> db = state.db.</span><span style="color:#56b6c2;">lock</span><span>().await;
</span><span> </span><span style="color:#c678dd;">let mut</span><span> response = db.</span><span style="color:#56b6c2;">query</span><span>(</span><span style="color:#98c379;">&quot;SELECT * FROM users&quot;</span><span>).await?;
</span><span>
</span><span> </span><span style="color:#c678dd;">let</span><span> result: Vec&lt;User&gt; = response.</span><span style="color:#56b6c2;">take</span><span>(</span><span style="color:#d19a66;">0</span><span>)?;
</span><span>
</span><span> Ok(Json(result))
</span><span>
</span><span>}
</span></code></pre>
<p>Now to create a user at localhost:3000/create/newusername</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span>async </span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">create_user</span><span>(Path(</span><span style="color:#e06c75;">uname</span><span>): Path&lt;String&gt;, State(</span><span style="color:#e06c75;">state</span><span>): State&lt;AppState&gt;) -&gt; Result&lt;impl IntoResponse, error::Error&gt;{
</span><span> </span><span style="color:#c678dd;">let</span><span> db = state.db.</span><span style="color:#56b6c2;">lock</span><span>().await;
</span><span>
</span><span> </span><span style="color:#c678dd;">let</span><span> newUser: Option&lt;User&gt; = db.</span><span style="color:#56b6c2;">create</span><span>(</span><span style="color:#98c379;">&quot;users&quot;</span><span>).</span><span style="color:#56b6c2;">content</span><span>(User {
</span><span> username: uname,
</span><span> }).await?;
</span><span>
</span><span> Ok(</span><span style="color:#98c379;">&quot;Success creating new user&quot;</span><span>)
</span><span>}
</span></code></pre>
<p>And we can delete a user at localhost:3000/delete/newusername</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span>async </span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">delete_user</span><span>(Path(</span><span style="color:#e06c75;">uname</span><span>): Path&lt;String&gt;, State(</span><span style="color:#e06c75;">state</span><span>): State&lt;AppState&gt;) -&gt; Result&lt;impl IntoResponse, error::Error&gt;{
</span><span> </span><span style="color:#c678dd;">let</span><span> db = state.db.</span><span style="color:#56b6c2;">lock</span><span>().await;
</span><span>
</span><span> db.</span><span style="color:#56b6c2;">query</span><span>(</span><span style="color:#98c379;">&quot;DELETE FROM users WHERE username = $username&quot;</span><span>)
</span><span> .</span><span style="color:#56b6c2;">bind</span><span>((</span><span style="color:#98c379;">&quot;username&quot;</span><span>, uname)).await?;
</span><span>
</span><span> Ok(</span><span style="color:#98c379;">&quot;Success deleting user&quot;</span><span>)
</span><span>}
</span></code></pre>
<p>With this basic setup in place, you can start experimenting with more complex queries, adding authentication, or introducing new features to your app. The full code is available on <a rel="noopener" target="_blank" href="https://github.com/spacedimp/Rust-Axum-SurrealDB-example/tree/main">Github</a></p>
<p>Happy coding!</p>
</content>
</entry>
<entry xml:lang="en">
<title>How to Properly Learn Rust Programming</title>
<published>2023-08-06T00:00:00+00:00</published>
<updated>2023-08-06T00:00:00+00:00</updated>
<author>
<name>
Unknown
</name>
</author>
<link rel="alternate" type="text/html" href="https://spacedimp.com/blog/how-to-properly-learn-rust/"/>
<id>https://spacedimp.com/blog/how-to-properly-learn-rust/</id>
<content type="html" xml:base="https://spacedimp.com/blog/how-to-properly-learn-rust/"><p>The goal, in this blog post, will be to help beginner Rust programmers overcome the notion that Rust is a difficult language.</p>
<p>First and foremost, I will advocate for the Rust book from Brown University over the regular one. Here: <a rel="noopener" target="_blank" href="https://rust-book.cs.brown.edu/">https://rust-book.cs.brown.edu/</a> (it requires you to scroll all the way down and accept to participate).</p>
<p>It provides a more thorough explanation of Rust and includes simple quizzes to test your new gained knowledge. The original book does not contain quizzes and so many people believe they understand Rust but are completely mistaken.</p>
<p>Alongside with the book, you'll want to test the examples using either a local programming environment or the online environment <a rel="noopener" target="_blank" href="https://play.rust-lang.org/">https://play.rust-lang.org/</a></p>
<h3 id="how-to-set-up-a-local-workspace-skip-if-you-prefer-the-online-rust-playground">How to set up a local workspace (skip if you prefer the online Rust Playground)</h3>
<p>I'm assuming Rust is already installed on your system.</p>
<p>Create a new directory</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">mkdir</span><span> project &amp;&amp; </span><span style="color:#56b6c2;">cd</span><span> project
</span></code></pre>
<p>Manually create a Cargo.toml for the workspace</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">vim</span><span> Cargo.toml
</span></code></pre>
<pre data-lang="toml" style="background-color:#282c34;color:#abb2bf;" class="language-toml "><code class="language-toml" data-lang="toml"><span>[workspace]
</span><span>
</span><span style="color:#e06c75;">members </span><span>= [
</span><span> </span><span style="color:#98c379;">&quot;app&quot;</span><span>,
</span><span> </span><span style="color:#98c379;">&quot;applib&quot;</span><span>,
</span><span>]
</span></code></pre>
<p>Initialize the binary directory and the library directory</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">cargo</span><span> init</span><span style="color:#e06c75;"> --bin</span><span> app &amp;&amp; </span><span style="color:#e06c75;">cargo</span><span> init</span><span style="color:#e06c75;"> --lib</span><span> applib
</span></code></pre>
<p>Make applib available inside of app by editing <code>app/Cargo.toml</code></p>
<pre data-lang="toml" style="background-color:#282c34;color:#abb2bf;" class="language-toml "><code class="language-toml" data-lang="toml"><span>[package]
</span><span style="color:#e06c75;">name </span><span>= </span><span style="color:#98c379;">&quot;app&quot;
</span><span style="color:#e06c75;">version </span><span>= </span><span style="color:#98c379;">&quot;0.1.0&quot;
</span><span style="color:#e06c75;">edition </span><span>= </span><span style="color:#98c379;">&quot;2021&quot;
</span><span>
</span><span>[dependencies]
</span><span style="font-style:italic;color:#5c6370;">#add this
</span><span style="color:#e06c75;">applib </span><span>= {</span><span style="color:#e06c75;">path </span><span>= </span><span style="color:#98c379;">&quot;../applib&quot;</span><span>}
</span></code></pre>
<p>Now applib's functions can be imported into the app binary. Edit <code>app/src/main.rs</code></p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c678dd;">use</span><span> applib;
</span><span>
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">main</span><span>() {
</span><span> </span><span style="font-style:italic;color:#5c6370;">// applib::add() is located in applib/src/lib.rs
</span><span> println!(</span><span style="color:#98c379;">&quot;100 + 100 = </span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, applib::add(</span><span style="color:#d19a66;">100</span><span>, </span><span style="color:#d19a66;">100</span><span>));
</span><span>}
</span></code></pre>
<p>and run it:</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">cargo</span><span> run</span><span style="color:#e06c75;"> --bin</span><span> app
</span><span>
</span><span style="font-style:italic;color:#5c6370;"># 200
</span></code></pre>
<br>
<h2 id="memory-safety">Memory safety</h2>
<p>I believe Rust's memory safe idiosyncrasies are what intimidate most people from this language. However, this is what makes it a safe language that doesn't require a garbage collector. It's essential to master this part of the language to write memory safe code.</p>
<p>The good news is that even if you write unsafe code it won't compile.</p>
<h3 id="ownership">Ownership</h3>
<p>Since there exists no garbage collector, owned variables are destructed once they go out of scope. Essentially, once a function or <a rel="noopener" target="_blank" href="https://doc.rust-lang.org/reference/expressions/block-expr.html">block expression</a> returns. Unless the variable is returned or the variable was passed by borrowing (also known as pass by reference).</p>
<p>The following variable will be immutable for the entire duration of the program.</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c678dd;">fn </span><span style="color:#61afef;">main</span><span>() {
</span><span> </span><span style="color:#c678dd;">let</span><span> num = </span><span style="color:#d19a66;">14</span><span>;
</span><span> println!(</span><span style="color:#98c379;">&quot;</span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, num); </span><span style="font-style:italic;color:#5c6370;">// prints 14
</span><span>}
</span></code></pre>
<blockquote>
<p>note: notice how num's type is implicitly assigned; a const variable would require explicitly assigning the type as so:</p>
<p>const NUM: i32 = 10;</p>
</blockquote>
<p>In order to make it mutable we must add the keyword mut:</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c678dd;">fn </span><span style="color:#61afef;">main</span><span>() {
</span><span> </span><span style="color:#c678dd;">let mut</span><span> num = </span><span style="color:#d19a66;">14</span><span>;
</span><span> num = </span><span style="color:#d19a66;">100</span><span>;
</span><span> println!(</span><span style="color:#98c379;">&quot;</span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, num); </span><span style="font-style:italic;color:#5c6370;">// prints 100
</span><span>}
</span></code></pre>
<p>Suppose we pass this variable into a function</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c678dd;">fn </span><span style="color:#61afef;">plusOne</span><span>(</span><span style="color:#e06c75;">num</span><span>: </span><span style="color:#c678dd;">i32</span><span>) {
</span><span> println!(</span><span style="color:#98c379;">&quot;</span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, num + </span><span style="color:#d19a66;">1</span><span>); </span><span style="font-style:italic;color:#5c6370;">// prints 101
</span><span>}
</span><span>
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">main</span><span>() {
</span><span> </span><span style="color:#c678dd;">let</span><span> num = </span><span style="color:#d19a66;">14</span><span>;
</span><span> plusOne(num);
</span><span> println!(</span><span style="color:#98c379;">&quot;</span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, num); </span><span style="font-style:italic;color:#5c6370;">// prints 100
</span><span>}
</span></code></pre>
<p>Notice how the code was able to call println! on num after calling the function plusOne.</p>
<p>Normally Rust would not compile this program because any variable passed into a function (without an ampersand &amp;) would destroy the variable.</p>
<p>However, Rust primitives such as u64 implement the <a rel="noopener" target="_blank" href="https://doc.rust-lang.org/std/marker/trait.Copy.html#implementors">Copy</a> trait. The function plusOne implicitly received a Copy of the variable num and thus we did not transfer ownership.</p>
<p>Let's see how Rust transfers ownership of a struct that doesn't implement Copy</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c678dd;">struct </span><span>Person{
</span><span> </span><span style="color:#e06c75;">name</span><span>: String
</span><span>}
</span><span>
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">getName</span><span>(</span><span style="color:#e06c75;">pers</span><span>: Person) {
</span><span> println!(</span><span style="color:#98c379;">&quot;name is </span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, pers.name); </span><span style="font-style:italic;color:#5c6370;">// drops the variable
</span><span>}
</span><span>
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">main</span><span>() {
</span><span> </span><span style="color:#c678dd;">let</span><span> Carl = Person{name: String::from(</span><span style="color:#98c379;">&quot;Carl&quot;</span><span>)};
</span><span> getName(Carl); </span><span style="font-style:italic;color:#5c6370;">// prints name is Carl
</span><span> </span><span style="font-style:italic;color:#5c6370;">// but we can no longer user Carl as it was dropped
</span><span> </span><span style="font-style:italic;color:#5c6370;">// println!(&quot;{}&quot;, Carl.name); would not work here as it did with num above
</span><span>}
</span></code></pre>
<p>In order to use Carl, after calling getName, we'd be required to pass it as a reference using &amp;</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span>...
</span><span>
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">getName</span><span>(</span><span style="color:#e06c75;">pers</span><span>: &amp;Person) {
</span><span> println!(</span><span style="color:#98c379;">&quot;name is </span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, pers.name); </span><span style="font-style:italic;color:#5c6370;">// does not drop the variable
</span><span>}
</span><span>
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">main</span><span>() {
</span><span> </span><span style="color:#c678dd;">let</span><span> Carl = Person{name: String::from(</span><span style="color:#98c379;">&quot;Carl&quot;</span><span>)};
</span><span> </span><span style="font-style:italic;color:#5c6370;">// placing an &amp; before the variable passes it as borrowed
</span><span> getName(&amp;Carl); </span><span style="font-style:italic;color:#5c6370;">// prints name is Carl
</span><span> println!(</span><span style="color:#98c379;">&quot;</span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, Carl.name); </span><span style="font-style:italic;color:#5c6370;">// prints Carl
</span><span>}
</span></code></pre>
<p>We could also transfer ownership of Carl to another variable just as we could into a function.</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span>...
</span><span>
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">main</span><span>() {
</span><span> </span><span style="color:#c678dd;">let</span><span> Carl = Person{name: String::from(</span><span style="color:#98c379;">&quot;Carl&quot;</span><span>)};
</span><span> </span><span style="font-style:italic;color:#5c6370;">// move ownership
</span><span> </span><span style="color:#c678dd;">let</span><span> Carl2 = Carl;
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6370;">// Carl is no longer available
</span><span> </span><span style="font-style:italic;color:#5c6370;">// Carl2 is available
</span><span> println!(</span><span style="color:#98c379;">&quot;</span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, Carl2.name); </span><span style="font-style:italic;color:#5c6370;">// prints Carl
</span><span>}
</span></code></pre>
<p>A variable can also be converted to mutable when moving it</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span>...
</span><span>
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">main</span><span>() {
</span><span> </span><span style="color:#c678dd;">let</span><span> Carl = Person{name: String::from(</span><span style="color:#98c379;">&quot;Carl&quot;</span><span>)};
</span><span> </span><span style="font-style:italic;color:#5c6370;">// move to mutable ownership
</span><span> </span><span style="color:#c678dd;">let mut</span><span> Carl2 = Carl;
</span><span> Carl2.name = </span><span style="color:#98c379;">&quot;Carl2&quot;</span><span>.</span><span style="color:#56b6c2;">to_string</span><span>();
</span><span>
</span><span> println!(</span><span style="color:#98c379;">&quot;</span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, Carl2.name); </span><span style="font-style:italic;color:#5c6370;">// prints Carl2
</span><span>}
</span></code></pre>
<p>Alternatively, a function can take a mutable borrow and change the value without deleting the variable.</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span>...
</span><span>
</span><span style="font-style:italic;color:#5c6370;">// changes the borrowed variable without dropping it
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">changeName</span><span>(</span><span style="color:#e06c75;">pers</span><span>: &amp;</span><span style="color:#c678dd;">mut</span><span> Person) {
</span><span> pers.name = </span><span style="color:#98c379;">&quot;Carl2&quot;</span><span>.</span><span style="color:#56b6c2;">to_string</span><span>();
</span><span>}
</span><span>
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">main</span><span>() {
</span><span> </span><span style="color:#c678dd;">let</span><span> Carl = Person{name: String::from(</span><span style="color:#98c379;">&quot;Carl&quot;</span><span>)};
</span><span> </span><span style="color:#c678dd;">let mut</span><span> Carl2 = Carl;
</span><span>
</span><span> changeName(&amp;</span><span style="color:#c678dd;">mut</span><span> Carl2);
</span><span> println!(</span><span style="color:#98c379;">&quot;</span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, Carl2.name); </span><span style="font-style:italic;color:#5c6370;">// prints Carl2
</span><span>}
</span></code></pre>
<p>However, if the variable is a primitive then ownership is not transferred.</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c678dd;">fn </span><span style="color:#61afef;">main</span><span>() {
</span><span> </span><span style="color:#c678dd;">let</span><span> a = </span><span style="color:#d19a66;">10</span><span>;
</span><span> </span><span style="font-style:italic;color:#5c6370;">// a is cloned and thus is not dropped
</span><span> </span><span style="color:#c678dd;">let</span><span> b = a;
</span><span>
</span><span> println!(</span><span style="color:#98c379;">&quot;</span><span style="color:#d19a66;">{a}</span><span style="color:#98c379;"> and </span><span style="color:#d19a66;">{b}</span><span style="color:#98c379;"> are clones&quot;</span><span>); </span><span style="font-style:italic;color:#5c6370;">// prints 10 and 10
</span><span>}
</span></code></pre>
<p>While a variable is borrowed mutably it cannot also be borrowed immutably. Only when the variable is no longer referenced by the borrower can it be again borrowed.</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c678dd;">struct </span><span>Person {
</span><span> </span><span style="color:#e06c75;">name</span><span>: String
</span><span>}
</span><span>
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">main</span><span>() {
</span><span> </span><span style="color:#c678dd;">let mut</span><span> Carl = Person{name: </span><span style="color:#98c379;">&quot;Carl&quot;</span><span>.</span><span style="color:#56b6c2;">to_string</span><span>()};
</span><span> </span><span style="font-style:italic;color:#5c6370;">// borrow Carl mutably
</span><span> </span><span style="color:#c678dd;">let</span><span> borrowCarlMutably = &amp;</span><span style="color:#c678dd;">mut</span><span> Carl;
</span><span> </span><span style="font-style:italic;color:#5c6370;">// Carl&#39;s name is indirectly changed to Carl2
</span><span> borrowCarlMutably.name = </span><span style="color:#98c379;">&quot;Carl2&quot;</span><span>.</span><span style="color:#56b6c2;">to_string</span><span>();
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6370;">// Carl cannot be assigned to an immutable variable as so:
</span><span> </span><span style="font-style:italic;color:#5c6370;">// let c = &amp;Carl;
</span><span> println!(</span><span style="color:#98c379;">&quot;</span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, borrowCarlMutably.name); </span><span style="font-style:italic;color:#5c6370;">// prints Carl2
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6370;">// Carl can now again be borrowed immutably because
</span><span> </span><span style="font-style:italic;color:#5c6370;">// borrowCarlMutably is no longer referenced
</span><span>
</span><span> </span><span style="color:#c678dd;">let</span><span> borrowCarlImmutably = &amp;Carl;
</span><span> println!(</span><span style="color:#98c379;">&quot;</span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, borrowCarlImmutably.name); </span><span style="font-style:italic;color:#5c6370;">// prints Carl2
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6370;">// Carl is still the owner as we only borrowed it above
</span><span> </span><span style="font-style:italic;color:#5c6370;">// Now that the mutable borrow is dropped we can use it again
</span><span> println!(</span><span style="color:#98c379;">&quot;</span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, Carl.name); </span><span style="font-style:italic;color:#5c6370;">// prints Carl2
</span><span>}
</span></code></pre>
<p>Variables stored as an <a rel="noopener" target="_blank" href="https://doc.rust-lang.org/std/option/">Option</a> type will be dropped in a match statement unless the unpacked variable is prefixed with <code>ref</code>.</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c678dd;">fn </span><span style="color:#61afef;">main</span><span>(){
</span><span> </span><span style="color:#c678dd;">let</span><span> name = Some(String::from(</span><span style="color:#98c379;">&quot;Carl&quot;</span><span>));
</span><span> </span><span style="color:#c678dd;">match</span><span> name {
</span><span> </span><span style="font-style:italic;color:#5c6370;">// notice the ref keyword
</span><span> </span><span style="font-style:italic;color:#5c6370;">// Using Some(n) .. will not compile
</span><span> Some(</span><span style="color:#c678dd;">ref</span><span> n) =&gt; println!(</span><span style="color:#98c379;">&quot;Hello </span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, n),
</span><span> _ =&gt; println!(</span><span style="color:#98c379;">&quot;no value&quot;</span><span>),
</span><span> }
</span><span> </span><span style="font-style:italic;color:#5c6370;">// if ref is not added, this would cause the program to not compile
</span><span> </span><span style="font-style:italic;color:#5c6370;">// since name would have been dropped in the match statement
</span><span> println!(</span><span style="color:#98c379;">&quot;Hello again </span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, name.</span><span style="color:#56b6c2;">unwrap</span><span>());
</span><span>}
</span></code></pre>
<h3 id="lifetimes">Lifetimes</h3>
<p>Rust also requires that a borrowed variable's data have a lifetime. Simply because you wouldn't want your borrowed variable to be dropped before you're done using it.</p>
<p>In fact the mutable variable we declared in the code right above was implementing lifetimes.</p>
<p>This code will NOT compile because the lifetime of borrowCarlMutably ends when we use Carl.</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span>...
</span><span>
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">main</span><span>() {
</span><span> </span><span style="color:#c678dd;">let mut</span><span> Carl = Person{name: </span><span style="color:#98c379;">&quot;Carl&quot;</span><span>.</span><span style="color:#56b6c2;">to_string</span><span>()};
</span><span> </span><span style="font-style:italic;color:#5c6370;">// borrow Carl mutably
</span><span> </span><span style="color:#c678dd;">let</span><span> borrowCarlMutably = &amp;</span><span style="color:#c678dd;">mut</span><span> Carl;
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6370;">// using Carl means borrowCarlMutably can no longer be used
</span><span> </span><span style="font-style:italic;color:#5c6370;">// because its lifetime has gone out of scope
</span><span> println!(</span><span style="color:#98c379;">&quot;</span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, Carl.name);
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6370;">// WRONG!
</span><span> </span><span style="font-style:italic;color:#5c6370;">// this should be moved above println before using Carl
</span><span> borrowCarlMutably.name = </span><span style="color:#98c379;">&quot;john&quot;</span><span>.</span><span style="color:#56b6c2;">to_string</span><span>();
</span><span>}
</span></code></pre>
<p>So far we haven't seen the syntax of lifetimes, even though I have passed borrowed variables into functions above.</p>
<p>The reason is that Rust elides (omits) them for for simple functions that don't cause the compiler to decide which to return.</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c678dd;">struct </span><span>Person {
</span><span> </span><span style="color:#e06c75;">name</span><span>: String
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5c6370;">// Elided
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">changeName</span><span>(</span><span style="color:#e06c75;">pers</span><span>: &amp;</span><span style="color:#c678dd;">mut</span><span> Person) {
</span><span> pers.name = </span><span style="color:#98c379;">&quot;joe&quot;</span><span>.</span><span style="color:#56b6c2;">to_string</span><span>();
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5c6370;">// Expanded
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">changeName</span><span>&lt;</span><span style="color:#c678dd;">&#39;a</span><span>&gt;(</span><span style="color:#e06c75;">pers</span><span>: &amp;</span><span style="color:#c678dd;">&#39;a mut</span><span> Person) {
</span><span> pers.name = </span><span style="color:#98c379;">&quot;joe&quot;</span><span>.</span><span style="color:#56b6c2;">to_string</span><span>();
</span><span>}
</span><span>
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">main</span><span>() {
</span><span> </span><span style="color:#c678dd;">let mut</span><span> Carl = Person{name: </span><span style="color:#98c379;">&quot;Carl&quot;</span><span>.</span><span style="color:#56b6c2;">to_string</span><span>()};
</span><span> changeName(&amp;</span><span style="color:#c678dd;">mut</span><span> Carl);
</span><span> println!(</span><span style="color:#98c379;">&quot;</span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, Carl.name);
</span><span>}
</span></code></pre>
<p>However more complicated functions that have borrowed variables with different lifetimes will require explicitly telling the compiler.</p>
<p>Note that an apostraphe is required for a lifetime's syntax however the name can be anything. It's common convention to use different letters for different lifetimes (e.g., <code>'a</code>, <code>'b</code>)</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c678dd;">struct </span><span>Person {
</span><span> </span><span style="color:#e06c75;">name</span><span>: String
</span><span>}
</span><span>
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">changeName</span><span>&lt;</span><span style="color:#c678dd;">&#39;a</span><span>, </span><span style="color:#c678dd;">&#39;b</span><span>&gt;(</span><span style="color:#e06c75;">pers</span><span>: &amp;</span><span style="color:#c678dd;">&#39;a mut</span><span> Person, </span><span style="color:#e06c75;">newName</span><span>: &amp;</span><span style="color:#c678dd;">&#39;b str</span><span>) {
</span><span> pers.name = newName.</span><span style="color:#56b6c2;">to_string</span><span>();
</span><span>}
</span><span>
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">main</span><span>() {
</span><span> </span><span style="color:#c678dd;">let mut</span><span> Carl = Person{name: </span><span style="color:#98c379;">&quot;Carl&quot;</span><span>.</span><span style="color:#56b6c2;">to_string</span><span>()};
</span><span>
</span><span> {
</span><span> </span><span style="font-style:italic;color:#5c6370;">// variables in this scope have different lifetimes
</span><span> </span><span style="font-style:italic;color:#5c6370;">// than those outside of {}
</span><span> </span><span style="color:#c678dd;">let</span><span> newName = </span><span style="color:#98c379;">&quot;Mario&quot;</span><span>;
</span><span> changeName(&amp;</span><span style="color:#c678dd;">mut</span><span> Carl, newName);
</span><span> }
</span><span> println!(</span><span style="color:#98c379;">&quot;</span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, Carl.name); </span><span style="font-style:italic;color:#5c6370;">// prints Mario
</span><span>}
</span></code></pre>
<p>Essentially, Rust is making sure that any borrowed value returned from the function will live at least as long as the lifetime of one of the inputs. The compiler can then make a decision as to whether your code is valid.</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c678dd;">struct </span><span>Person {
</span><span> </span><span style="color:#e06c75;">name</span><span>: String
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5c6370;">// now we return a borrowed variable with a lifetime
</span><span style="font-style:italic;color:#5c6370;">// of newName
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">changeName</span><span>&lt;</span><span style="color:#c678dd;">&#39;a</span><span>, </span><span style="color:#c678dd;">&#39;b</span><span>&gt;(</span><span style="color:#e06c75;">pers</span><span>: &amp;</span><span style="color:#c678dd;">&#39;a mut</span><span> Person, </span><span style="color:#e06c75;">newName</span><span>: &amp;</span><span style="color:#c678dd;">&#39;b str</span><span>) -&gt; &amp;</span><span style="color:#c678dd;">&#39;b str </span><span>{
</span><span> pers.name = newName.</span><span style="color:#56b6c2;">to_string</span><span>();
</span><span> &amp;newName
</span><span>}
</span><span>
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">main</span><span>() {
</span><span> </span><span style="color:#c678dd;">let mut</span><span> Carl = Person{name: </span><span style="color:#98c379;">&quot;Carl&quot;</span><span>.</span><span style="color:#56b6c2;">to_string</span><span>()};
</span><span> {
</span><span> </span><span style="color:#c678dd;">let</span><span> newName = </span><span style="color:#98c379;">&quot;Mario&quot;</span><span>;
</span><span> </span><span style="color:#c678dd;">let </span><span>_ = changeName(&amp;</span><span style="color:#c678dd;">mut</span><span> Carl, newName);
</span><span> }
</span><span> println!(</span><span style="color:#98c379;">&quot;</span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, Carl.name);
</span><span>}
</span></code></pre>
<p>There exists a reserved lifetime called <code>'static</code> that signifies to the compiler that the variable will live for the entire lifetime of the program. The variable will be embedded into the binary.</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span style="color:#c678dd;">static </span><span style="color:#d19a66;">GLOBAL</span><span>: &amp;</span><span style="color:#c678dd;">&#39;static str </span><span>= </span><span style="color:#98c379;">&quot;global static variable&quot;</span><span>;
</span><span>
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">main</span><span>() {
</span><span> println!(</span><span style="color:#98c379;">&quot;</span><span style="color:#d19a66;">{}</span><span style="color:#98c379;">&quot;</span><span>, </span><span style="color:#d19a66;">GLOBAL</span><span>);
</span><span>}
</span></code></pre>
<p>In my opinion, much of the struggle that people have wrestling with the borrow checker stems from gaps in knowledge related to ownership and lifetimes.</p>
<p>The quizzes in the brown.edu git book definitely help filling those gaps.</p>
<p>If Rust still seems confusing then I recommend <a rel="noopener" target="_blank" href="https://fasterthanli.me/articles/a-half-hour-to-learn-rust">A half-hour to learn Rust</a>.</p>
</content>
</entry>
<entry xml:lang="en">
<title>Dockerless, Elixir Web Application using Podman and Plug</title>
<published>2023-06-06T00:00:00+00:00</published>
<updated>2023-06-06T00:00:00+00:00</updated>
<author>
<name>
Unknown
</name>
</author>
<link rel="alternate" type="text/html" href="https://spacedimp.com/blog/dockerless-setting-up-an-elixir-webapp-using-podman-and-plug/"/>
<id>https://spacedimp.com/blog/dockerless-setting-up-an-elixir-webapp-using-podman-and-plug/</id>
<content type="html" xml:base="https://spacedimp.com/blog/dockerless-setting-up-an-elixir-webapp-using-podman-and-plug/"><h2 id="demonstration-of-how-to-containerize-an-elixir-web-app-rootless-for-security-inside-of-podman">Demonstration of how to containerize an Elixir web app. Rootless, for security, inside of Podman.</h2>
<p>Also we'll be using the <a rel="noopener" target="_blank" href="https://hexdocs.pm/plug/readme.html">Plug</a> module to keep things simple.
This module is at the heart of popular Elixir web frameworks such as <a rel="noopener" target="_blank" href="https://hexdocs.pm/phoenix/plug.html">Phoenix</a>.</p>
<blockquote>
<p>Plug lives at the heart of Phoenix's HTTP layer, and Phoenix puts Plug front and center.</p>
</blockquote>
<br>
<h3 id="setup">Setup:</h3>
<p>First, we need Podman so head on over to the <a rel="noopener" target="_blank" href="https://podman.io/docs/installation">official</a> instllation instructions and then come back.</p>
<p>Next we need an image that we can install Elixir on.. I'll use Alpine Linux.</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">podman</span><span> search alpine</span><span style="color:#e06c75;"> --filter</span><span> is-official
</span></code></pre>
<p>Go ahead and pull it:</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">podman</span><span> image pull docker.io/library/alpine
</span></code></pre>
<p>Check that it installed:</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">podman</span><span> image list alpine
</span></code></pre>
<p>Now run it (more networking options <a rel="noopener" target="_blank" href="https://docs.oracle.com/en/operating-systems/oracle-linux/podman/podman-ConfiguringNetworkingforPodman.html#configuring-podman-networking">here</a>):</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="font-style:italic;color:#5c6370;"># -p host-port:container-port
</span><span style="color:#e06c75;">podman</span><span> run</span><span style="color:#e06c75;"> -dit --name</span><span> alp</span><span style="color:#e06c75;"> --network</span><span>=bridge</span><span style="color:#e06c75;"> -p</span><span> 7070:7070 alpine /bin/ash
</span></code></pre>
<br>
<h3 id="connect-to-container-and-install-elixir">Connect to container and install Elixir:</h3>
<p>Make sure the container is running first:</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">podman</span><span> ps
</span></code></pre>
<p>If you don't see any output then you must start the container:</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">podman</span><span> start alp
</span></code></pre>
<p>Attach to container:</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">podman</span><span> attach alp
</span></code></pre>
<br>
<p>You should now be logged in as root (don't worry this root can't do harm outside of the container)</p>
<p>Add a user that has sudo (inside the container):</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">adduser</span><span> alpine
</span><span>
</span><span style="font-style:italic;color:#5c6370;"># enter a password for this user
</span><span>
</span><span style="font-style:italic;color:#5c6370;"># you could also change root&#39;s password with this command: passwd
</span></code></pre>
<p>Install the sudo program:</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">apk</span><span> add sudo
</span></code></pre>
<p>Use visudo to allow the group wheel to use sudo:</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">visudo
</span><span>
</span><span style="font-style:italic;color:#5c6370;"># uncomment the line
</span><span style="font-style:italic;color:#5c6370;"># %wheel ALL=(ALL:ALL) ALL
</span></code></pre>
<p>You'll need to know a few vim commands to use visudo. If you really don't know then
search up a quick tutorial. Hey you might end up liking vim.</p>
<p>Now add the new user to the wheel group:</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">adduser</span><span> alp wheel &amp;&amp; </span><span style="color:#e06c75;">su</span><span> alp
</span></code></pre>
<p>You should be the alp user now and not root.</p>
<p>Install Elixir:</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">sudo</span><span> apk add elixir &amp;&amp; </span><span style="color:#56b6c2;">cd </span><span style="color:#e06c75;">~
</span></code></pre>
<br>
<h3 id="create-an-elixir-web-app">Create an Elixir web app:</h3>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">mix</span><span> new myapp</span><span style="color:#e06c75;"> --sup
</span></code></pre>
<p>We'll now install the Plug dependency:</p>
<p>Edit <code>mix.ex</code></p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">vi</span><span> mix.ex
</span><span>
</span><span style="font-style:italic;color:#5c6370;"># and make sure deps has plug as shown below
</span></code></pre>
<pre data-lang="md" style="background-color:#282c34;color:#abb2bf;" class="language-md "><code class="language-md" data-lang="md"><span>defp deps do
</span><span> [
</span><span> {:plug, &quot;~&gt; 1.14&quot;},
</span><span> {:plug_cowboy, &quot;~&gt; 2.0&quot;}
</span><span> ]
</span><span>end
</span></code></pre>
<p>Now install plug:</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">mix</span><span> deps.get
</span><span>
</span><span style="font-style:italic;color:#5c6370;"># You&#39;ll be asked to install hex. Type Y
</span></code></pre>
<p>Great!</p>
<p>Now let's make the app fault tolerant by having the application supervise it:</p>
<p>Edit <code>lib/myapp/application.ex</code></p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">def</span><span> start(_type, _args) </span><span style="color:#c678dd;">do
</span><span> </span><span style="color:#e06c75;">children</span><span> = [
</span><span> </span><span style="color:#e06c75;">{Plug.Cowboy,</span><span> scheme: :http, plug: Myapp, options: </span><span style="color:#c678dd;">[</span><span>port: 7070</span><span style="color:#c678dd;">]</span><span>}
</span><span> </span><span style="color:#e06c75;">]
</span></code></pre>
<p>Lastly we'll add a router to the web app, Edit <code>lib/myapp.ex</code></p>
<pre data-lang="elixir" style="background-color:#282c34;color:#abb2bf;" class="language-elixir "><code class="language-elixir" data-lang="elixir"><span style="color:#c678dd;">defmodule </span><span style="color:#e5c07b;">Myapp </span><span style="color:#c678dd;">do
</span><span style="color:#c678dd;">use </span><span style="color:#e5c07b;">Plug</span><span>.</span><span style="color:#e5c07b;">Router
</span><span>
</span><span style="font-style:italic;color:#5c6370;"> # matches a route
</span><span> plug </span><span style="color:#98c379;">:match
</span><span style="font-style:italic;color:#5c6370;"> # then forwards it to a dispatch
</span><span> plug </span><span style="color:#98c379;">:dispatch
</span><span>
</span><span> get </span><span style="color:#98c379;">&quot;/&quot; </span><span style="color:#c678dd;">do
</span><span> send_resp(conn, </span><span style="color:#d19a66;">200</span><span>, </span><span style="color:#98c379;">&quot;homepage&quot;</span><span>)
</span><span> </span><span style="color:#c678dd;">end
</span><span>
</span><span> get </span><span style="color:#98c379;">&quot;/hello/:name&quot; </span><span style="color:#c678dd;">do
</span><span> send_resp(conn, </span><span style="color:#d19a66;">200</span><span>, </span><span style="color:#98c379;">&quot;Hi #{</span><span>name}</span><span style="color:#98c379;">&quot;</span><span>)
</span><span> </span><span style="color:#c678dd;">end
</span><span>
</span><span style="font-style:italic;color:#5c6370;"> # 404
</span><span> get _ </span><span style="color:#c678dd;">do
</span><span> send_resp(conn, </span><span style="color:#d19a66;">404</span><span>, </span><span style="color:#98c379;">&quot;404 not found&quot;</span><span>)
</span><span> </span><span style="color:#c678dd;">end
</span><span style="color:#c678dd;">end
</span></code></pre>
<p>Start it up:</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">mix</span><span> run</span><span style="color:#e06c75;"> --no-halt
</span></code></pre>
<p>Open up your browser and head over to <code>localhost:7070/hello/world</code></p>
<br>
<h3 id="build-an-image-from-it">Build an image from it</h3>
<p>Now we should compile the web app into a binary and also create an
image from the container to make it portable.</p>
<p>First let's compile the code.</p>
<p>In the container's terminal run:</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">MIX_ENV</span><span>=</span><span style="color:#98c379;">prod
</span><span style="color:#e06c75;">RELEASE_NAME</span><span>=</span><span style="color:#98c379;">myapp
</span><span style="color:#e06c75;">mix</span><span> release.init
</span></code></pre>
<p>compile:</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">mix</span><span> release
</span></code></pre>
<p>You should now have a binary located at:</p>
<p><code>_build/dev/rel/myapp/bin/myapp</code></p>
<p>You can run it as so:</p>
<p><code>_build/dev/rel/myapp/bin/myapp start</code></p>
<p>We can now exit the container so that we can build an image with this
binary installed.</p>
<p><code>exit &amp;&amp; exit</code></p>
<p>In your host terminal run:</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">podman</span><span> commit alp alp:v2
</span></code></pre>
<p>Now we can start up the new container with the binary running by default:</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">podman</span><span> run</span><span style="color:#e06c75;"> -dit --network</span><span>=bridge</span><span style="color:#e06c75;"> -p</span><span> 7070:7070</span><span style="color:#e06c75;"> --name</span><span> alp2 alp:v2 /home/alpine/myapp/_build/dev/rel/myapp/bin/myapp start
</span></code></pre>
<h3 id="wrap-up">Wrap up</h3>
<p>The main benefits of using Podman over Docker are security related. I won't rehash it all here so if you
want to learn more check out linode's <a rel="noopener" target="_blank" href="https://www.linode.com/docs/guides/podman-vs-docker/">explanation</a>.</p>
<p>I suggest learning more about plug and its router from <a rel="noopener" target="_blank" href="https://elixirschool.com/blog/building-apps-with-plug-router/">elixirschool</a>.</p>
<br>
</content>
</entry>
<entry xml:lang="en">
<title>Using Rust, Tauri, and SvelteKit to Build a Note Taking App</title>
<published>2023-04-05T00:00:00+00:00</published>
<updated>2023-04-05T00:00:00+00:00</updated>
<author>
<name>
Unknown
</name>
</author>
<link rel="alternate" type="text/html" href="https://spacedimp.com/blog/using-rust-tauri-and-sveltekit-to-build-a-note-taking-app/"/>
<id>https://spacedimp.com/blog/using-rust-tauri-and-sveltekit-to-build-a-note-taking-app/</id>
<content type="html" xml:base="https://spacedimp.com/blog/using-rust-tauri-and-sveltekit-to-build-a-note-taking-app/"><h2 id="in-this-blog-post-i-ll-be-guiding-you-through-the-process-of-building-a-note-taking-app-using-tauri-move-over-electron">In this blog post, I'll be guiding you through the process of building a note taking app using Tauri. Move over Electron :)</h2>
<p><img src="/img/gif/noteapp.gif" alt="Alt text" title="demo of an app built using Rust, Tauri, and SvelteKit" /></p>
<p>Tauri allows us to build fast, cross-platform, and small sized apps using HTML, CSS, and JavaScript.</p>
<p>It accomplishes this by using <a rel="noopener" target="_blank" href="https://tauri.app/v1/references/webview-versions/">WebViews</a>. A WebView lets you embed
web content (HTML,CSS, JavaScript) into an application without needing a full-fledged web browser.</p>
<p>Rust is used for the backend logic and <a rel="noopener" target="_blank" href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/Svelte_getting_started">SvelteKit</a> for the frontend.</p>
<p>Each OS uses a different WebView rendering engine:</p>
<ul>
<li>Windows uses WebView2</li>
<li>Linux uses WebKit</li>
</ul>
<h2 id="setting-up-the-project">Setting up the project</h2>
<p>Make sure Rust and the Tauri dependencies are installed as described <a rel="noopener" target="_blank" href="https://tauri.app/v1/guides/getting-started/prerequisites">here</a>.</p>
<p>SvelteKit requires Node.js. I install it using Fedora's package manager.</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">sudo</span><span> dnf install nodejs
</span></code></pre>
<p>Instead of npm, I'll install pnpm as the Node.js package manager</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">sudo</span><span> npm install</span><span style="color:#e06c75;"> -g</span><span> pnpm
</span></code></pre>
<p>Now we can initialize a new svelte project.</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">$</span><span> mkdir notes &amp;&amp; </span><span style="color:#56b6c2;">cd</span><span> notes
</span><span style="color:#e06c75;">$</span><span> pnpm create svelte
</span><span>
</span><span style="color:#e06c75;">//</span><span> hit enter to create the project in the current directory
</span><span>
</span><span style="color:#e06c75;">//</span><span> use down arrow key to select Skeleton project
</span><span>
</span><span style="color:#e06c75;">//</span><span> use down arrow key to select Yes, using Typescript syntax
</span><span>
</span><span style="color:#e06c75;">//</span><span> use space bar to select
</span><span style="color:#e06c75;">// </span><span>* Add ESLint for code linting
</span><span style="color:#e06c75;">// </span><span>* Add Prettier for code formatting
</span><span>
</span><span style="color:#e06c75;">//</span><span> @next gets latest version
</span><span style="color:#e06c75;">$</span><span> pnpm add</span><span style="color:#e06c75;"> -D</span><span> @sveltejs/adapter-static@next
</span></code></pre>
<p>edit svelte.config.js</p>
<pre data-lang="js" style="background-color:#282c34;color:#abb2bf;" class="language-js "><code class="language-js" data-lang="js"><span style="font-style:italic;color:#5c6370;">// change adapter-auto to adapter-static
</span><span style="color:#c678dd;">import </span><span style="color:#e06c75;">adapter </span><span style="color:#c678dd;">from </span><span style="color:#98c379;">&#39;@sveltejs/adapter-static&#39;</span><span>;
</span><span>
</span><span>...
</span><span>
</span><span style="font-style:italic;color:#5c6370;">// add prerender entries
</span><span>kit: {
</span><span> adapter: </span><span style="color:#61afef;">adapter</span><span>(),
</span><span> prerender: {
</span><span> entries: [</span><span style="color:#98c379;">&#39;*&#39;</span><span>, </span><span style="color:#98c379;">&#39;/edit/*&#39;</span><span>]
</span><span> }
</span><span>}
</span><span>
</span></code></pre>
<p>Disable SSR by creating src/routes/+layout.ts</p>
<pre data-lang="ts" style="background-color:#282c34;color:#abb2bf;" class="language-ts "><code class="language-ts" data-lang="ts"><span style="color:#c678dd;">export const </span><span style="color:#e06c75;">prerender </span><span>= </span><span style="color:#d19a66;">true</span><span>;
</span><span style="color:#c678dd;">export const </span><span style="color:#e06c75;">ssr </span><span>= </span><span style="color:#d19a66;">false</span><span>;
</span></code></pre>
<p>Check your node.js version and make sure pnpm uses the correct one</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">$</span><span> node</span><span style="color:#e06c75;"> -v
</span><span style="color:#e06c75;">v18.15.0
</span><span>
</span><span style="color:#e06c75;">//</span><span> edit .npmrc
</span><span style="color:#e06c75;">$</span><span> vi .npmrc
</span><span>
</span><span style="color:#e06c75;">//</span><span> add your version number as so
</span><span style="color:#e06c75;">use-node-version</span><span>=</span><span style="color:#98c379;">18.15.0
</span></code></pre>
<p>Setup Tauri</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">$</span><span> pnpm add</span><span style="color:#e06c75;"> -D</span><span> @tauri-apps/cli
</span><span style="color:#e06c75;">$</span><span> pnpm tauri init
</span><span>
</span><span style="color:#e06c75;">//</span><span> What is your app name? notes
</span><span>
</span><span style="color:#e06c75;">//</span><span> What should the window title be? notes
</span><span>
</span><span style="color:#e06c75;">//</span><span> Where are your web assets ..? ../build
</span><span>
</span><span style="color:#e06c75;">//</span><span> What is the URL of your dev server? http://localhost:5173
</span><span>
</span><span style="color:#e06c75;">//</span><span> What is your frontend dev command? pnpm run dev
</span><span>
</span><span style="color:#e06c75;">//</span><span> What is your frontend build command? pnpm run build
</span></code></pre>
<p>Run the app</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">//</span><span> will be slow the first time running but after much faster
</span><span style="color:#e06c75;">$</span><span> pnpm tauri dev
</span></code></pre>
<h2 id="setting-up-components">Setting up components</h2>
<p>I recommend for beginners to go through the official Svelte tutorial <a rel="noopener" target="_blank" href="https://svelte.dev/tutorial/basics">here</a> to grasp its fundamentals.</p>
<p>This is an excerpt of what a component is:</p>
<blockquote>
<p>In Svelte, an application is composed from one or more components. A component is a reusable self-contained block of code that encapsulates HTML, CSS and JavaScript that belong together, written into a .svelte file. The 'hello world' example in the code editor is a simple component.</p>
</blockquote>
<p>I'll be creating two components inside of <code>src/lib</code>. One called <code>Notes.svelte</code> will display all notes created. The other called <code>CreateNote.svelte</code> will be a text box where we can add new notes.</p>
<p>Create <code>src/lib/Notes.svelte</code></p>
<pre data-lang="ts" style="background-color:#282c34;color:#abb2bf;" class="language-ts "><code class="language-ts" data-lang="ts"><span>&lt;</span><span style="color:#e06c75;">script lang</span><span>=</span><span style="color:#98c379;">&quot;ts&quot;</span><span>&gt;
</span><span> </span><span style="color:#c678dd;">let </span><span style="color:#e06c75;">title </span><span>= </span><span style="color:#98c379;">&quot;First Note&quot;</span><span>;
</span><span>&lt;/</span><span style="color:#e06c75;">script</span><span>&gt;
</span><span>
</span><span>&lt;</span><span style="color:#e06c75;">div id</span><span>=</span><span style="color:#98c379;">&quot;notes&quot;</span><span>&gt;
</span><span> &lt;p&gt; {</span><span style="color:#e06c75;">title</span><span>} &lt;/</span><span style="color:#e06c75;">p</span><span>&gt;
</span><span>&lt;/</span><span style="color:#e06c75;">div</span><span>&gt;
</span></code></pre>
<p>Create <code>src/lib/CreateNote.svelte</code></p>
<pre data-lang="ts" style="background-color:#282c34;color:#abb2bf;" class="language-ts "><code class="language-ts" data-lang="ts"><span>&lt;</span><span style="color:#e06c75;">script lang</span><span>=</span><span style="color:#98c379;">&quot;ts&quot;</span><span>&gt;
</span><span> </span><span style="color:#c678dd;">let </span><span style="color:#e06c75;">newNote</span><span>;
</span><span> </span><span style="color:#c678dd;">let </span><span style="color:#e06c75;">newTitle</span><span>;
</span><span>&lt;/</span><span style="color:#e06c75;">script</span><span>&gt;
</span><span>
</span><span>&lt;</span><span style="color:#e06c75;">div id</span><span>=</span><span style="color:#98c379;">&quot;new-note&quot;</span><span>&gt;
</span><span> &lt;h1&gt; </span><span style="color:#e06c75;">Create a Note </span><span>&lt;/</span><span style="color:#e06c75;">h1</span><span>&gt;
</span><span> &lt;</span><span style="color:#e06c75;">textarea </span><span>bind:</span><span style="color:#e06c75;">value</span><span>={</span><span style="color:#e06c75;">newTitle</span><span>} </span><span style="color:#e06c75;">id</span><span>=</span><span style="color:#98c379;">&quot;new-note-title&quot; </span><span style="color:#e06c75;">placeholder</span><span>=</span><span style="color:#98c379;">&quot;Note title&quot;</span><span>&gt;&lt;/textarea&gt;
</span><span> &lt;</span><span style="color:#e06c75;">textarea </span><span>bind:</span><span style="color:#e06c75;">value</span><span>={</span><span style="color:#e06c75;">newNote</span><span>} </span><span style="color:#e06c75;">id</span><span>=</span><span style="color:#98c379;">&quot;new-note-box&quot; </span><span style="color:#e06c75;">placeholder</span><span>=</span><span style="color:#98c379;">&quot;Note body&quot;</span><span>&gt;&lt;/textarea&gt;
</span><span>&lt;/</span><span style="color:#e06c75;">div</span><span>&gt;
</span></code></pre>
<p>A page is a route to a certain path. <code>src/routes/+page.svelte</code> will be the homepage, for instance.</p>
<p>Import the components into the page by editing <code>src/routes/+page.svelte</code></p>
<pre data-lang="ts" style="background-color:#282c34;color:#abb2bf;" class="language-ts "><code class="language-ts" data-lang="ts"><span>&lt;</span><span style="color:#e06c75;">script lang</span><span>=</span><span style="color:#98c379;">&quot;ts&quot;</span><span>&gt;
</span><span> </span><span style="color:#c678dd;">import </span><span style="color:#e06c75;">Notes </span><span style="color:#c678dd;">from </span><span style="color:#98c379;">&#39;$lib/Notes.svelte&#39;
</span><span> </span><span style="color:#c678dd;">import </span><span style="color:#e06c75;">CreateNote </span><span style="color:#c678dd;">from </span><span style="color:#98c379;">&#39;$lib/CreateNote.svelte&#39;
</span><span>&lt;/</span><span style="color:#e06c75;">script</span><span>&gt;
</span><span>
</span><span>&lt;</span><span style="color:#e06c75;">div id</span><span>=</span><span style="color:#98c379;">&quot;container&quot;</span><span>&gt;
</span><span> &lt;</span><span style="color:#e06c75;">CreateNote</span><span>/&gt;
</span><span> &lt;</span><span style="color:#e06c75;">Notes</span><span>/&gt;
</span><span>&lt;/</span><span style="color:#e06c75;">div</span><span>&gt;
</span></code></pre>
<p>You could place CSS style tags into each Page or Component, but I prefer a global CSS file.</p>
<p>Create <code>static/global.css</code></p>
<pre data-lang="CSS" style="background-color:#282c34;color:#abb2bf;" class="language-CSS "><code class="language-CSS" data-lang="CSS"><span style="font-style:italic;color:#5c6370;">/* CSS reset */
</span><span style="color:#e06c75;">*</span><span style="color:#c678dd;">, </span><span style="color:#e06c75;">*</span><span style="color:#d19a66;">::</span><span style="color:#c678dd;">before, </span><span style="color:#e06c75;">*</span><span style="color:#d19a66;">::</span><span style="color:#c678dd;">after </span><span>{
</span><span> box-sizing: border-box;
</span><span>}
</span><span>
</span><span style="color:#e06c75;">* </span><span>{
</span><span> margin: </span><span style="color:#d19a66;">0</span><span>;
</span><span>}
</span><span>
</span><span style="color:#e06c75;">html</span><span style="color:#c678dd;">, </span><span style="color:#e06c75;">body </span><span>{
</span><span> height: </span><span style="color:#d19a66;">100%</span><span>;
</span><span>}
</span><span>
</span><span style="color:#e06c75;">body </span><span>{
</span><span> line-height: </span><span style="color:#d19a66;">1.5</span><span>;
</span><span> -webkit-font-smoothing: antialiased;
</span><span>}
</span><span>
</span><span style="color:#e06c75;">img</span><span style="color:#c678dd;">, </span><span style="color:#e06c75;">picture</span><span style="color:#c678dd;">, </span><span style="color:#e06c75;">video</span><span style="color:#c678dd;">, </span><span style="color:#e06c75;">canvas</span><span style="color:#c678dd;">, </span><span style="color:#e06c75;">svg </span><span>{
</span><span> display: block;
</span><span> max-width: </span><span style="color:#d19a66;">100%</span><span>;
</span><span>}
</span><span>
</span><span style="color:#e06c75;">input</span><span style="color:#c678dd;">, </span><span style="color:#e06c75;">button</span><span style="color:#c678dd;">, </span><span style="color:#e06c75;">textarea</span><span style="color:#c678dd;">, </span><span style="color:#e06c75;">select </span><span>{
</span><span> font: inherit;
</span><span>}
</span><span>
</span><span style="color:#e06c75;">p</span><span style="color:#c678dd;">, </span><span style="color:#e06c75;">h1</span><span style="color:#c678dd;">, </span><span style="color:#e06c75;">h2</span><span style="color:#c678dd;">, </span><span style="color:#e06c75;">h3</span><span style="color:#c678dd;">, </span><span style="color:#e06c75;">h4</span><span style="color:#c678dd;">, </span><span style="color:#e06c75;">h5</span><span style="color:#c678dd;">, </span><span style="color:#e06c75;">h6 </span><span>{
</span><span> overflow-wrap: break-word;
</span><span>}
</span><span>
</span><span style="color:#d19a66;">#root</span><span style="color:#c678dd;">, </span><span style="color:#d19a66;">#__next </span><span>{
</span><span> isolation: isolate;
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5c6370;">/* Component and Page CSS */
</span><span>
</span><span style="color:#d19a66;">#container </span><span>{
</span><span> box-sizing: border-box;
</span><span> width: </span><span style="color:#d19a66;">100%</span><span>;
</span><span> height: </span><span style="color:#d19a66;">100%</span><span>;
</span><span> display: flex;
</span><span> white-space: nowrap;
</span><span>}
</span><span>
</span><span style="color:#d19a66;">#notes </span><span>{
</span><span> background: </span><span style="color:#56b6c2;">#eee</span><span>;
</span><span>}
</span></code></pre>
<p>Add it to <code>src/app.html</code> inside of the head tag</p>
<pre data-lang="html" style="background-color:#282c34;color:#abb2bf;" class="language-html "><code class="language-html" data-lang="html"><span>&lt;</span><span style="color:#e06c75;">link </span><span style="color:#d19a66;">rel</span><span>=</span><span style="color:#98c379;">&quot;stylesheet&quot; </span><span style="color:#d19a66;">type</span><span>=</span><span style="color:#98c379;">&quot;text/css&quot; </span><span style="color:#d19a66;">href</span><span>=</span><span style="color:#98c379;">&quot;%sveltekit.assets%/global.css&quot;</span><span>&gt;
</span></code></pre>
<p>I'll be saving the notes in the frontend. For this we require Tauri's frontend API.</p>
<pre data-lang="bash" style="background-color:#282c34;color:#abb2bf;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#e06c75;">$</span><span> pnpm add</span><span style="color:#e06c75;"> -D</span><span> @tauri-apps/api
</span></code></pre>
<p>We must tell Tauri which paths are available to our app. In this case I'll be writing to a file called db.bson in the user's home/notes-db directory.</p>
<p>Edit <code>src-tauri/tauri.conf.json</code></p>
<pre data-lang="json" style="background-color:#282c34;color:#abb2bf;" class="language-json "><code class="language-json" data-lang="json"><span style="color:#98c379;">&quot;tauri&quot;</span><span>: {
</span><span> </span><span style="color:#98c379;">&quot;allowlist&quot;</span><span>: {
</span><span> </span><span style="color:#98c379;">&quot;all&quot;</span><span>: </span><span style="color:#d19a66;">false</span><span>,
</span><span> </span><span style="color:#98c379;">&quot;fs&quot;</span><span>: {
</span><span> </span><span style="color:#98c379;">&quot;scope&quot;</span><span>: [</span><span style="color:#98c379;">&quot;$HOME/notes-db/*&quot;</span><span>, </span><span style="color:#98c379;">&quot;$HOME/notes-db&quot;</span><span>],
</span><span> </span><span style="color:#98c379;">&quot;all&quot;</span><span>: </span><span style="color:#d19a66;">true
</span><span> },
</span><span> </span><span style="color:#98c379;">&quot;path&quot;</span><span>: {
</span><span> </span><span style="color:#98c379;">&quot;all&quot;</span><span>: </span><span style="color:#d19a66;">true
</span><span> }
</span><span> },
</span></code></pre>
<p>Also scroll down in the conf.json file and find "identifier" . It should be unique to your app. I'll set it to com.random.random.</p>
<pre data-lang="json" style="background-color:#282c34;color:#abb2bf;" class="language-json "><code class="language-json" data-lang="json"><span style="color:#98c379;">&quot;bundle&quot;</span><span>: {
</span><span> </span><span style="color:#98c379;">&quot;identifier&quot;</span><span>: </span><span style="color:#98c379;">&quot;com.random.random&quot;</span><span>,
</span><span>}
</span></code></pre>
<h2 id="handling-data-in-the-backend">Handling data in the backend</h2>
<p>I've chosen to store the data as bson (Binary JSON). Read more about bson <a rel="noopener" target="_blank" href="https://www.mongodb.com/json-and-bson">here</a>. Basically it's how MongoDB stores JSON data on disk as binary.</p>
<p>Tauri lets the frontend pass data back and forth to the backend (Rust) using Tauri commands.</p>
<p>For the sake of brevity I'll just show the complete Rust backend code.</p>
<p>Edit <code>src-tauri/src/main.rs</code> with:</p>
<pre data-lang="rust" style="background-color:#282c34;color:#abb2bf;" class="language-rust "><code class="language-rust" data-lang="rust"><span>#![</span><span style="color:#e06c75;">cfg_attr</span><span>(
</span><span> </span><span style="color:#e06c75;">all</span><span>(</span><span style="color:#e06c75;">not</span><span>(debug_assertions), target_os = </span><span style="color:#98c379;">&quot;windows&quot;</span><span>),
</span><span> windows_subsystem = </span><span style="color:#98c379;">&quot;windows&quot;
</span><span>)]
</span><span>
</span><span style="color:#c678dd;">use </span><span>bson::{Document};
</span><span>
</span><span style="color:#c678dd;">use </span><span>serde::{Serialize, Deserialize};
</span><span style="color:#c678dd;">use </span><span>std::io::{Cursor};
</span><span>
</span><span style="color:#c678dd;">use</span><span> serde_json;
</span><span>
</span><span>#[</span><span style="color:#e06c75;">derive</span><span>(Serialize, Deserialize, Debug)]
</span><span style="color:#c678dd;">struct </span><span>Note {
</span><span> </span><span style="color:#e06c75;">bson_uuid</span><span>: String,
</span><span> </span><span style="color:#e06c75;">date_time</span><span>: bson::DateTime,
</span><span> </span><span style="color:#e06c75;">title</span><span>: String,
</span><span> </span><span style="color:#e06c75;">body</span><span>: String,
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5c6370;">// builds a new Note object for the froteend
</span><span style="font-style:italic;color:#5c6370;">// we then convert it to a bson document
</span><span style="font-style:italic;color:#5c6370;">// lastly we convert it into a vec of bytes to store on disk (frontend handles appending then saving this to disk)
</span><span>#[</span><span style="color:#e06c75;">tauri</span><span>::</span><span style="color:#e06c75;">command</span><span>]
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">saveNote</span><span>(</span><span style="color:#e06c75;">title</span><span>: &amp;</span><span style="color:#c678dd;">str</span><span>, </span><span style="color:#e06c75;">body</span><span>: &amp;</span><span style="color:#c678dd;">str</span><span>) -&gt; Vec&lt;</span><span style="color:#c678dd;">u8</span><span>&gt; {
</span><span> </span><span style="color:#c678dd;">let</span><span> note = Note { bson_uuid: bson::Uuid::new().</span><span style="color:#56b6c2;">to_string</span><span>(), date_time: bson::DateTime::now(), title: title.</span><span style="color:#56b6c2;">to_string</span><span>(), body: body.</span><span style="color:#56b6c2;">to_string</span><span>() };
</span><span> </span><span style="color:#c678dd;">let</span><span> note_doc = bson::to_document(&amp;note).</span><span style="color:#56b6c2;">unwrap</span><span>();
</span><span>
</span><span> </span><span style="color:#c678dd;">return </span><span>bson::to_vec(&amp;note_doc).</span><span style="color:#56b6c2;">unwrap</span><span>();
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5c6370;">// after the frontend edits or deletes a note
</span><span style="font-style:italic;color:#5c6370;">// it must be saved back to db.bson
</span><span>#[</span><span style="color:#e06c75;">tauri</span><span>::</span><span style="color:#e06c75;">command</span><span>]
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">editNote</span><span>(</span><span style="color:#e06c75;">data</span><span>: &amp;</span><span style="color:#c678dd;">str</span><span>) -&gt; Vec&lt;</span><span style="color:#c678dd;">u8</span><span>&gt; {
</span><span> </span><span style="color:#c678dd;">let</span><span> vecNotes: Vec&lt;Note&gt; = serde_json::from_str(data).</span><span style="color:#56b6c2;">unwrap</span><span>();
</span><span> </span><span style="color:#c678dd;">let</span><span> vecDocs: Vec&lt;Document&gt; = vecNotes.</span><span style="color:#56b6c2;">iter</span><span>().</span><span style="color:#56b6c2;">map</span><span>(|</span><span style="color:#e06c75;">e</span><span>| bson::to_document(&amp;e).</span><span style="color:#56b6c2;">unwrap</span><span>() ).</span><span style="color:#56b6c2;">collect</span><span>();
</span><span> </span><span style="color:#c678dd;">let</span><span> docsArray: Vec&lt;</span><span style="color:#c678dd;">u8</span><span>&gt; = vecDocs.</span><span style="color:#56b6c2;">clone</span><span>().</span><span style="color:#56b6c2;">into_iter</span><span>().</span><span style="color:#56b6c2;">flat_map</span><span>(|</span><span style="color:#e06c75;">e</span><span>| bson::to_vec(&amp;e.</span><span style="color:#56b6c2;">clone</span><span>()).</span><span style="color:#56b6c2;">unwrap</span><span>()).</span><span style="color:#56b6c2;">collect</span><span>();
</span><span>
</span><span> </span><span style="color:#c678dd;">return</span><span> docsArray;
</span><span>}
</span><span>
</span><span style="font-style:italic;color:#5c6370;">// loading the raw data from db.bson requires us to convert it to JSON
</span><span style="font-style:italic;color:#5c6370;">// for the frontend to interact with
</span><span>#[</span><span style="color:#e06c75;">tauri</span><span>::</span><span style="color:#e06c75;">command</span><span>]
</span><span style="color:#c678dd;">fn </span><span style="color:#61afef;">loadNotes</span><span>(</span><span style="color:#e06c75;">data</span><span>: &amp;</span><span style="color:#c678dd;">str</span><span>) -&gt; String{
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6370;">// check if database is empty.
</span><span> </span><span style="font-style:italic;color:#5c6370;">// Return if it is otherwise the program will crash
</span><span> </span><span style="color:#c678dd;">if</span><span> data.</span><span style="color:#56b6c2;">chars</span><span>().</span><span style="color:#56b6c2;">count</span><span>() == </span><span style="color:#d19a66;">0 </span><span>{
</span><span> </span><span style="color:#c678dd;">return </span><span>String::from(</span><span style="color:#98c379;">&quot;no data&quot;</span><span>);
</span><span> }
</span><span>
</span><span> </span><span style="font-style:italic;color:#5c6370;">// frontend passes the database as a string array of bytes
</span><span> </span><span style="font-style:italic;color:#5c6370;">// parse it into bytes