summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Cargo.toml16
-rw-r--r--fonts/fira-sans.bold-italic.ttfbin0 -> 447536 bytes
-rw-r--r--fonts/fira-sans.bold.ttfbin0 -> 438028 bytes
-rw-r--r--fonts/fira-sans.italic.ttfbin0 -> 411640 bytes
-rw-r--r--fonts/fira-sans.regular.ttfbin0 -> 403924 bytes
-rw-r--r--src/lib.rs4
-rw-r--r--src/model.rs76
-rw-r--r--src/pdfgen.rs48
-rw-r--r--src/validation.rs37
-rw-r--r--src/view.rs83
-rw-r--r--static/index.html14
12 files changed, 280 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..96ef6c0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/target
+Cargo.lock
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..2eaf0bf
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "bcard-wasm-webapp"
+version = "0.1.0"
+authors = ["jelemux <jeremias.weber@protonmail.com>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[lib]
+crate-type = ["cdylib", "rlib"]
+
+[dependencies]
+yew = "0.17.3" # react-like frontend framework
+wasm-bindgen = "0.2.68" # enables interaction with js
+chrono = "0.4.19" # date & time
+genpdf = { path = "../genpdf-rs" } # pdf generation \ No newline at end of file
diff --git a/fonts/fira-sans.bold-italic.ttf b/fonts/fira-sans.bold-italic.ttf
new file mode 100644
index 0000000..97b52ef
--- /dev/null
+++ b/fonts/fira-sans.bold-italic.ttf
Binary files differ
diff --git a/fonts/fira-sans.bold.ttf b/fonts/fira-sans.bold.ttf
new file mode 100644
index 0000000..95e1660
--- /dev/null
+++ b/fonts/fira-sans.bold.ttf
Binary files differ
diff --git a/fonts/fira-sans.italic.ttf b/fonts/fira-sans.italic.ttf
new file mode 100644
index 0000000..f5e4914
--- /dev/null
+++ b/fonts/fira-sans.italic.ttf
Binary files differ
diff --git a/fonts/fira-sans.regular.ttf b/fonts/fira-sans.regular.ttf
new file mode 100644
index 0000000..d9fdc0e
--- /dev/null
+++ b/fonts/fira-sans.regular.ttf
Binary files differ
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..0b497a1
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,4 @@
+mod view;
+mod model;
+mod validation;
+mod pdfgen; \ No newline at end of file
diff --git a/src/model.rs b/src/model.rs
new file mode 100644
index 0000000..cac0820
--- /dev/null
+++ b/src/model.rs
@@ -0,0 +1,76 @@
+use chrono::NaiveDateTime;
+use crate::validation::{self, *};
+
+pub struct BCard {
+ name: Option<Name>,
+ nickname: Option<String>,
+ label: Option<TypedProperty<String>>,
+ address: Option<TypedProperty<Address>>,
+ emails: Option<Vec<TypedProperty<String>>>,
+ title: Option<String>,
+ role: Option<String>,
+ organization: Option<String>,
+ urls: Option<Vec<TypedProperty<String>>>,
+ telephones: Option<Vec<TypedProperty<String>>>,
+ revision: Option<NaiveDateTime>,
+}
+
+impl BCard {
+ fn new() -> Self {
+ Self {
+ name: None,
+ nickname: None,
+ label: None,
+ address: None,
+ emails: None,
+ title: None,
+ role: None,
+ organization: None,
+ urls: None,
+ telephones: None,
+ revision: None,
+ }
+ }
+}
+
+impl Validation for BCard {
+ fn validate(&self) -> Result<(), ValidationError> {
+ let mut result = Ok(());
+ result = match &self.name {
+ Some(n) => validation::add_results(result, n.validate()),
+ None => Err( ValidationError{ messages: vec![String::from("Name cannot be empty")] } ),
+ };
+ // TODO add some more validation
+ result
+ }
+}
+
+pub struct Name {
+ prefix: Option<String>,
+ first_name: Option<String>,
+ middle_name: Option<String>,
+ family_name: Option<String>,
+ suffix: Option<String>,
+}
+
+impl Validation for Name {
+ fn validate(&self) -> std::result::Result<(), ValidationError> { todo!() }
+}
+
+pub enum WorkHomeType {
+ Home,
+ Work,
+}
+
+pub struct TypedProperty<T> {
+ p_type: Option<WorkHomeType>,
+ value: T,
+}
+
+pub struct Address {
+ street: Option<String>,
+ city: Option<String>,
+ locality: Option<String>,
+ postal_code: Option<String>,
+ country: Option<String>,
+} \ No newline at end of file
diff --git a/src/pdfgen.rs b/src/pdfgen.rs
new file mode 100644
index 0000000..b8c7f7d
--- /dev/null
+++ b/src/pdfgen.rs
@@ -0,0 +1,48 @@
+use genpdf::Element as _;
+use genpdf::{elements, style, fonts};
+use crate::model::BCard;
+
+pub fn genpdf(bcard: BCard) -> Vec<u8> {
+ let regular_bytes = include_bytes!("../fonts/fira-sans.regular.ttf");
+ let regular_font_data = fonts::FontData::new(regular_bytes.to_vec(), None).expect("font data should be correct");
+
+ let bold_bytes = include_bytes!("../fonts/fira-sans.bold.ttf");
+ let bold_font_data = fonts::FontData::new(bold_bytes.to_vec(), None).expect("font data should be correct");
+
+ let italic_bytes = include_bytes!("../fonts/fira-sans.italic.ttf");
+ let italic_font_data = fonts::FontData::new(italic_bytes.to_vec(), None).expect("font data should be correct");
+
+ let bold_italic_bytes = include_bytes!("../fonts/fira-sans.bold-italic.ttf");
+ let bold_italic_font_data = fonts::FontData::new(bold_italic_bytes.to_vec(), None).expect("font data should be correct");
+
+ let font_family = fonts::FontFamily{ regular: regular_font_data, bold: bold_font_data, italic: italic_font_data, bold_italic: bold_italic_font_data };
+
+ let mut doc = genpdf::Document::new(font_family);
+
+ doc.set_title("BCard test");
+ doc.set_minimal_conformance();
+ doc.set_margins(10);
+ doc.set_line_spacing(1.25);
+
+ #[cfg(feature = "hyphenation")]
+ {
+ use hyphenation::Load;
+
+ doc.set_hyphenator(
+ hyphenation::Standard::from_embedded(hyphenation::Language::EnglishUS)
+ .expect("Failed to load hyphenation data"),
+ );
+ }
+
+ doc.push(
+ elements::Paragraph::new("genpdf Demo Document")
+ .aligned(elements::Alignment::Center)
+ .styled(style::Style::new().bold().with_font_size(20)),
+ );
+
+ // TODO fill doc with real data
+
+ let mut buf: Vec<u8> = Vec::new();
+ doc.render(&mut buf);
+ buf
+} \ No newline at end of file
diff --git a/src/validation.rs b/src/validation.rs
new file mode 100644
index 0000000..715b472
--- /dev/null
+++ b/src/validation.rs
@@ -0,0 +1,37 @@
+
+
+pub trait Validation {
+ fn validate(&self) -> Result<(), ValidationError>;
+}
+
+#[derive(Debug)]
+pub struct ValidationError {
+ messages: Vec<String>,
+}
+
+impl std::fmt::Display for ValidationError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ for msg in &self.messages {
+ write!(f, "{}\n", msg)?;
+ }
+ Ok(())
+ }
+}
+
+impl std::error::Error for ValidationError { }
+
+pub fn add_results(first: Result<(), ValidationError>, second: Result<(), ValidationError>) -> Result<(), ValidationError> {
+ if first.is_ok() && second.is_ok() {
+ Ok(())
+ } else if first.is_ok() && second.is_err() {
+ second
+ } else if first.is_err() && second.is_ok() {
+ first
+ } else {
+ let mut first = first.err().unwrap();
+ let mut second = second.err().unwrap();
+ first.messages.append(&mut second.messages);
+
+ Err( ValidationError{ messages: first.messages } )
+ }
+} \ No newline at end of file
diff --git a/src/view.rs b/src/view.rs
new file mode 100644
index 0000000..81634ed
--- /dev/null
+++ b/src/view.rs
@@ -0,0 +1,83 @@
+use crate::model::BCard;
+use wasm_bindgen::prelude::*;
+use yew::prelude::*;
+
+struct Form {
+ link: ComponentLink<Self>,
+ bcard: BCard,
+}
+
+impl Component for Form { // probably not necessary but who knows
+ type Message = ();
+ type Properties = ();
+
+ fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
+ Self { link }
+ }
+
+ fn update(&mut self, _: Self::Message) -> ShouldRender {
+ false
+ }
+
+ fn change(&mut self, _: Self::Properties) -> ShouldRender {
+ false
+ }
+
+ fn view(&self) -> Html {
+ html! {
+ <BCardForm/>
+ }
+ }
+}
+
+// example
+
+struct Model {
+ link: ComponentLink<Self>,
+ value: i64,
+}
+
+enum Msg {
+ AddOne,
+ Input(BCard),
+}
+
+impl Component for Model {
+ type Message = Msg;
+ type Properties = ();
+
+ fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
+ Self {
+ link,
+ value: 0,
+ }
+ }
+
+ fn update(&mut self, msg: Self::Message) -> ShouldRender {
+ match msg {
+ Msg::AddOne => self.value += 1
+ }
+ true
+ }
+
+ fn change(&mut self, _props: Self::Properties) -> ShouldRender {
+ // Should only return "true" if new properties are different to
+ // previously received properties.
+ // This component has no properties so we will always return "false".
+ false
+ }
+
+ fn view(&self) -> Html {
+ html! {
+ <div>
+ <button onclick=self.link.callback(|_| Msg::AddOne)>{ "+1" }</button>
+ <p>{ self.value }</p>
+ </div>
+ }
+ }
+}
+
+#[wasm_bindgen(start)]
+pub fn run_app() {
+ App::<Model>::new().mount_to_body();
+} \ No newline at end of file
diff --git a/static/index.html b/static/index.html
new file mode 100644
index 0000000..b0f1634
--- /dev/null
+++ b/static/index.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>BCard Wasm Web App</title>
+ <script type="module">
+ import init from "../pkg/bcard_wasm_webapp.js"
+ init()
+ </script>
+ </head>
+ <body>
+ <noscript>This page contains webassembly and javascript content, please enable javascript in your browser.</noscript>
+ </body>
+</html> \ No newline at end of file