summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
5 files changed, 248 insertions, 0 deletions
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