diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/view/address.rs | 29 | ||||
| -rw-r--r-- | src/view/main.rs | 281 | ||||
| -rw-r--r-- | src/view/name.rs | 57 | ||||
| -rw-r--r-- | src/view/telephone.rs | 29 | ||||
| -rw-r--r-- | src/viewmodel/address.rs | 2 | ||||
| -rw-r--r-- | src/viewmodel/mod.rs | 2 | ||||
| -rw-r--r-- | src/viewmodel/name.rs | 4 | ||||
| -rw-r--r-- | src/viewmodel/telephone.rs | 15 | ||||
| -rw-r--r-- | src/viewmodel/vcard.rs | 32 | 
9 files changed, 294 insertions, 157 deletions
diff --git a/src/view/address.rs b/src/view/address.rs index 23b697a..a16d93a 100644 --- a/src/view/address.rs +++ b/src/view/address.rs @@ -1,5 +1,9 @@ +use yew::services::ConsoleService; +use yewtil::ptr::Irc; +use yewtil::ptr::Mrc;  use super::WeakComponentLink;  use yew::prelude::*; +use yewtil::NeqAssign;  use crate::viewmodel::address::*;  use crate::viewmodel::VCardPropertyInputObject;  use super::VCardPropertyInputComponent; @@ -8,7 +12,7 @@ use crate::viewmodel::Error;  pub struct AddressView {      props: Props, -    value: Address, +    value: Mrc<Address>,      error: Option<Error>,  } @@ -28,13 +32,19 @@ pub enum Msg {  #[derive(Clone, PartialEq, Properties)]  pub struct Props { -    pub generated: Callback<Address>, +    pub generated: Callback<Irc<Address>>,      pub weak_link: WeakComponentLink<AddressView>,  }  impl VCardPropertyInputComponent<Address> for AddressView {      fn get_input_object(&self) -> Address { -        self.value.clone() +        match self.value.irc().try_unwrap() { +            Ok(address) => address, +            Err(_) => { +                ConsoleService::error("Couldn't unwrap address"); +                Address::new() +            }, +        }      }      fn get_title(&self) -> String {          "Address".to_string() @@ -51,7 +61,7 @@ impl Component for AddressView {          props.weak_link.borrow_mut().replace(link);          Self {              props, -            value: Address::new(), +            value: Mrc::new(Address::new()),              error: None,          }      } @@ -66,19 +76,14 @@ impl Component for AddressView {              Msg::UpdateCountry(c) => self.value.country = c,              Msg::ToggleWork => self.value.work = !self.value.work,              Msg::ToggleHome => self.value.home = !self.value.home, -            Generate => { -                self.props.generated.emit(self.value); +            Msg::Generate => { +                self.props.generated.emit(self.value.irc());              },          };          true      }      fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool {  -        if self.props != props { -            self.props = props; -            true -        } else { -            false -        } +        self.props.neq_assign(props)      }      fn view(&self) -> yew::virtual_dom::VNode {          let link = self.props.weak_link.borrow().clone().unwrap(); diff --git a/src/view/main.rs b/src/view/main.rs index e926504..b1c0dda 100644 --- a/src/view/main.rs +++ b/src/view/main.rs @@ -1,6 +1,8 @@ +use yew::services::ConsoleService; +use yewtil::ptr::Irc; +use crate::viewmodel::vcard::VCardData;  use crate::viewmodel::Error;  use crate::view::telephone::{self,TelephoneView}; -use std::collections::HashSet;  use super::WeakComponentLink;  use super::name::{self,NameView};  use super::address::{self,AddressView}; @@ -9,13 +11,15 @@ use genpdf::{elements, style, fonts};  use qrcodegen::QrCode;  use qrcodegen::QrCodeEcc;  use yew::prelude::*; -use vobject::Vcard; +use yewtil::ptr::Mrc;  use vobject::vcard::VcardBuilder;  use vobject::parameters; +use chrono::prelude::*;  use crate::viewmodel::name::Name;  use crate::viewmodel::address::Address;  use crate::viewmodel::telephone::Telephone;  use crate::viewmodel::utility::*; +use boolinator::Boolinator;  pub struct MainView { @@ -23,7 +27,7 @@ pub struct MainView {      error: Option<Error>,      download: Option<Download>,      selected_option: DownloadOption, -    vcard_builder: VcardBuilder, +    vcard_data: Mrc<VCardData>,      name_links: Vec<WeakComponentLink<NameView>>,      address_links: Vec<WeakComponentLink<AddressView>>, @@ -40,10 +44,9 @@ pub enum Msg {      ChangeDownloadOption(DownloadOption),      Generate, -    GeneratedFormattedName(String), -    GeneratedName(Name), -    GeneratedAddress(Address), -    GeneratedTelephone(Telephone), +    GeneratedName(Irc<Name>), +    GeneratedAddress(Irc<Address>), +    GeneratedTelephone(Irc<Telephone>),      GenerationComplete,      Nope, @@ -59,7 +62,7 @@ impl Component for MainView {              error: None,               download: None,               selected_option: DownloadOption::VCard, -            vcard_builder: VcardBuilder::new(), +            vcard_data: Mrc::new(VCardData::new()),              name_links: vec![WeakComponentLink::default()],              address_links: vec![WeakComponentLink::default()], @@ -126,26 +129,14 @@ impl Component for MainView {                  shouldrender = true;              }, -            Msg::GeneratedFormattedName(formatted_name) => { - -                self.answer_count += 1; - -                self.vcard_builder = self.vcard_builder.with_fullname(formatted_name); - -                shouldrender = true; -            },              Msg::GeneratedName(name) => {                  self.answer_count += 1; -                self.vcard_builder = self.vcard_builder.with_name( -                    parameters!(), -                    name.last_name.is_empty().then(|| name.last_name), -                    name.first_name.is_empty().then(|| name.first_name), -                    name.middle_name.is_empty().then(|| name.middle_name), -                    name.prefix.is_empty().then(|| name.prefix), -                    name.suffix.is_empty().then(|| name.suffix) -                ); +                match self.vcard_data.get_mut() { +                    Some(vcard_data) => vcard_data.add_name(name), +                    None => ConsoleService::info("Error in GeneratedName: Couldn't get mutable borrow of VCardData"), +                };                  shouldrender = true; @@ -154,9 +145,10 @@ impl Component for MainView {                  self.answer_count += 1; -                self.vcard_builder = self.vcard_builder.with_adr( -                    parameters!(), -                ); +                match self.vcard_data.get_mut() { +                    Some(vcard_data) => vcard_data.add_address(address), +                    None => ConsoleService::info("Error in GeneratedAddress: Couldn't get mutable borrow of VCardData"), +                };                  shouldrender = true;              }, @@ -164,31 +156,10 @@ impl Component for MainView {                  self.answer_count += 1; -                match telephone { -                    Ok(telephone) => { -                        match self.vcard { -                            Some(vcard) => { -                                match vcard.telephones { -                                    Some(telephones) => { -                                        telephones.insert(telephone); -                                    }, -                                    None => { -                                        let telephones = { -                                            let mut telephones = HashSet::new(); -                                            telephones.insert(telephone); -                                             -                                            telephones -                                        }; -                         -                                        vcard.telephones = Some(vcard::Set::from_hash_set(telephones).unwrap()); -                                    } -                                }; -                            }, -                            None => (), -                        };         -                    }, -                    Err(_) => (), -                } +                match self.vcard_data.get_mut() { +                    Some(vcard_data) => vcard_data.add_telephone(telephone), +                    None => ConsoleService::info("Error in GeneratedTelephone: Couldn't get mutable borrow of VCardData"), +                };                  shouldrender = true;              }, @@ -196,43 +167,167 @@ impl Component for MainView {                  self.answer_count = 0; -                match self.vcard.clone() { -                    Some(vcard) => { -                        match self.selected_option { -                            DownloadOption::VCard => { -                                self.download = Some( -                                    Download { -                                        file_name: String::from("VCard.vcs"), -                                        content: vcard.to_string(), -                                        mime_type: MimeType::VCard, -                                    } -                                ); -                            }, -                            DownloadOption::QrCode => { -                                match QrCode::encode_text(&vcard.to_string(), QrCodeEcc::Low) { -                                    Ok(qr) => self.download = Some( -                                        Download { -                                            file_name: String::from("QR-Code VCard.svg"), -                                            content: qr.to_svg_string(4), -                                            mime_type: MimeType::SVG, -                                        } -                                    ), -                                    Err(_) => self.error = Some( -                                        Error{ -                                            msg: String::from("Sorry, VCard is too long!"), -                                        } -                                    ), -                                }; -                            }, -                            _ => (), -                        }; -                     -                        self.vcard = None; -                        shouldrender = true; +                let vcard_data = match self.vcard_data.irc().try_unwrap() { +                    Ok(data) => data, +                    Err(err) => {  +                        ConsoleService::error(&format!("Error when unwrapping VCardData: {:?}", err)); +                        VCardData::new()                      }, -                    None => shouldrender = false, // what TODO here? +                }; + +                let mut builder = VcardBuilder::new(); + +                for name in vcard_data.names { + +                    builder = builder +                    .with_fullname( +                        name.generate_fn() +                    ) +                    .with_name( +                        parameters!(), +                        name.last_name.is_empty().as_some(name.last_name.clone()), +                        name.first_name.is_empty().as_some(name.first_name.clone()), +                        name.middle_name.is_empty().as_some(name.middle_name.clone()), +                        name.prefix.is_empty().as_some(name.prefix.clone()), +                        name.suffix.is_empty().as_some(name.suffix.clone()) +                    ); +                } + +                for address in vcard_data.addresses { +                    let mut types = String::new(); +                    if address.work { +                        types.push_str("WORK"); +                    } +                    if address.home { +                        if types.is_empty() { +                            types.push(','); +                        } +                        types.push_str("HOME") +                    } + +                    builder = builder.with_adr( +                        parameters!("TYPE" => types), +                        address.post_office_box.is_empty().as_some(address.post_office_box.clone()), +                        address.extension.is_empty().as_some(address.extension.clone()), +                        address.street.is_empty().as_some(address.street.clone()), +                        address.locality.is_empty().as_some(address.locality.clone()), +                        address.region.is_empty().as_some(address.region.clone()), +                        address.code.is_empty().as_some(address.code.clone()), +                        address.country.is_empty().as_some(address.country.clone()), +                    );                  } +                for telephone in vcard_data.telephones { +                    let mut types = String::new(); +                    if telephone.work { +                        types.push_str("WORK"); +                    } +                    if telephone.home { +                        if types.is_empty() { +                            types.push(','); +                        } +                        types.push_str("HOME") +                    } +                    if telephone.text { +                        if types.is_empty() { +                            types.push(','); +                        } +                        types.push_str("TEXT") +                    } +                    if telephone.voice { +                        if types.is_empty() { +                            types.push(','); +                        } +                        types.push_str("VOICE") +                    } +                    if telephone.fax { +                        if types.is_empty() { +                            types.push(','); +                        } +                        types.push_str("FAX") +                    } +                    if telephone.cell { +                        if types.is_empty() { +                            types.push(','); +                        } +                        types.push_str("CELL") +                    } +                    if telephone.video { +                        if types.is_empty() { +                            types.push(','); +                        } +                        types.push_str("VIDEO") +                    } +                    if telephone.pager { +                        if types.is_empty() { +                            types.push(','); +                        } +                        types.push_str("PAGER") +                    } +                    if telephone.text_phone { +                        if types.is_empty() { +                            types.push(','); +                        } +                        types.push_str("TEXTPHONE") +                    } + +                    builder = builder.with_tel( +                        parameters!("TYPE" => types), +                        telephone.number.clone(), +                    ); +                } +                     + +                let rev = Local::now(); +                 +                match builder +                    .with_version("4.0".to_string()) +                    .with_rev(format!("{}", rev)) +                    .build() { +                        Ok(vcard) => { +                            match self.selected_option { +                                DownloadOption::VCard => { +                                    self.download = Some( +                                        Download { +                                            file_name: String::from("VCard.vcs"), +                                            content: vobject::write_component(&vcard), +                                            mime_type: MimeType::VCard, +                                        } +                                    ); +                                }, +                                DownloadOption::QrCode => { +                                    match QrCode::encode_text(&vobject::write_component(&vcard), QrCodeEcc::Low) { +                                        Ok(qr) => self.download = Some( +                                            Download { +                                                file_name: String::from("QR-Code VCard.svg"), +                                                content: qr.to_svg_string(4), +                                                mime_type: MimeType::SVG, +                                            } +                                        ), +                                        Err(_) => self.error = Some( +                                            Error{ +                                                msg: String::from("Sorry, VCard is too long!"), +                                            } +                                        ), +                                    }; +                                }, +                                _ => (), +                            }; +                        }, +                        Err(err) => self.error = Some( +                            Error{ +                                msg: err.to_string(), +                            } +                        ), +                    }; + +                match self.vcard_data.get_mut() { +                    Some(vcard_data) => *vcard_data = VCardData::new(), +                    None => ConsoleService::info("Couldn't reset VCardData"), +                }; + +                shouldrender = true; +              },              Msg::Nope => shouldrender = false,          }; @@ -286,12 +381,8 @@ impl Component for MainView {                                  for self.name_links.iter().map(|link|                                      html!{                                          <NameView   weak_link=link  -                                                    generated_fn=self.link.callback( -                                                        |fmn: Result<properties::FormattedName,()>|  -                                                            Msg::GeneratedFormattedName(fmn) -                                                    ) -                                                    generated_name=self.link.callback( -                                                        |n: Result<properties::Name,()>|  +                                                    generated=self.link.callback( +                                                        |n: Irc<Name>|                                                               Msg::GeneratedName(n)                                                      )                                          /> @@ -304,7 +395,7 @@ impl Component for MainView {                                      html!{                                          <AddressView    weak_link=link                                                          generated=self.link.callback( -                                                            |a: Result<properties::Address,()>| +                                                            |a: Irc<Address>|                                                                  Msg::GeneratedAddress(a)                                                          )                                          /> @@ -317,7 +408,7 @@ impl Component for MainView {                                      html!{                                          <TelephoneView  weak_link=link                                                          generated=self.link.callback( -                                                            |t: Result<properties::Telephone,()>| +                                                            |t: Irc<Telephone>|                                                                  Msg::GeneratedTelephone(t)                                                          )                                          /> @@ -326,7 +417,7 @@ impl Component for MainView {                              }                              <div class="block level-left"> -                                <button onclick=self.link.callback(|_| Msg::Generate) class="button is-primary level-item" /> +                                <button onclick=self.link.callback(|_| Msg::Generate) class="button is-primary level-item">{ "Generate" }</button>                                  <div class="select level-item">                                      <select id="download_options" onchange=download_options> diff --git a/src/view/name.rs b/src/view/name.rs index bee9256..5b64bae 100644 --- a/src/view/name.rs +++ b/src/view/name.rs @@ -1,14 +1,17 @@ +use yew::services::ConsoleService; +use yewtil::ptr::Irc; +use yewtil::ptr::Mrc;  use crate::viewmodel::Error;  use crate::view::WeakComponentLink;  use yew::prelude::*; -use vobject::Property; +use yewtil::NeqAssign;  use crate::viewmodel::name::*;  use crate::viewmodel::VCardPropertyInputObject;  use super::VCardPropertyInputComponent;  pub struct NameView {      props: Props, -    value: Name, +    value: Mrc<Name>,      error: Option<Error>,  } @@ -24,14 +27,19 @@ pub enum Msg {  #[derive(Clone, PartialEq, Properties)]  pub struct Props { -    pub generated_name: Callback<Name>, -    pub generated_fn: Callback<String>, +    pub generated: Callback<Irc<Name>>,      pub weak_link: WeakComponentLink<NameView>,  }  impl VCardPropertyInputComponent<Name> for NameView {      fn get_input_object(&self) -> Name { -        self.value.clone() +        match self.value.clone().try_unwrap() { +            Ok(name) => name, +            Err(_) => { +                ConsoleService::error("Couldn't unwrap name"); +                Name::new() +            }, +        }      }      fn get_title(&self) -> String {          "Name".to_string() @@ -48,31 +56,40 @@ impl Component for NameView {          props.weak_link.borrow_mut().replace(link);          Self {              props, -            value: Name::new(), +            value: Mrc::new(Name::new()),              error: None,          }      }      fn update(&mut self, msg: <Self as yew::Component>::Message) -> bool {          match msg { -            Msg::UpdatePrefix(p) => self.value.prefix = p, -            Msg::UpdateFirstName(f) => self.value.first_name = f, -            Msg::UpdateMiddleName(m) => self.value.middle_name = m, -            Msg::UpdateLastName(l) => self.value.last_name = l, -            Msg::UpdateSuffix(s) => self.value.suffix = s, -            Generate => { -                self.props.generated_fn.emit(self.value.generate_fn()); -                self.props.generated_name.emit(self.value); +            Msg::UpdatePrefix(p) => match self.value.get_mut() { +                Some(value) => value.prefix = p, +                None => ConsoleService::info("Couldn't get mutable reference to name"), +            }, +            Msg::UpdateFirstName(f) => match self.value.get_mut() { +                Some(value) => value.first_name = f, +                None => ConsoleService::info("Couldn't get mutable reference to name"), +            }, +            Msg::UpdateMiddleName(m) => match self.value.get_mut() { +                Some(value) => value.middle_name = m, +                None => ConsoleService::info("Couldn't get mutable reference to name"), +            }, +            Msg::UpdateLastName(l) => match self.value.get_mut() { +                Some(value) => value.last_name = l, +                None => ConsoleService::info("Couldn't get mutable reference to name"), +            }, +            Msg::UpdateSuffix(s) => match self.value.get_mut() { +                Some(value) => value.suffix = s, +                None => ConsoleService::info("Couldn't get mutable reference to name"), +            }, +            Msg::Generate => { +                self.props.generated.emit(self.value.irc());              },          };          true      }      fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool {  -        if self.props != props { -            self.props = props; -            true -        } else { -            false -        } +        self.props.neq_assign(props)      }      fn view(&self) -> yew::virtual_dom::VNode {          let link = self.props.weak_link.borrow().clone().unwrap(); diff --git a/src/view/telephone.rs b/src/view/telephone.rs index 26d7806..ca58538 100644 --- a/src/view/telephone.rs +++ b/src/view/telephone.rs @@ -1,19 +1,22 @@ +use yew::services::ConsoleService; +use yewtil::ptr::Irc; +use yewtil::ptr::Mrc;  use crate::view::WeakComponentLink;  use crate::viewmodel::Error;  use yew::prelude::*; +use yewtil::NeqAssign;  use crate::viewmodel::telephone::*;  use crate::viewmodel::VCardPropertyInputObject;  use super::VCardPropertyInputComponent;  pub struct TelephoneView {      props: Props, -    value: Telephone, +    value: Mrc<Telephone>,      error: Option<Error>,  }  pub enum Msg {      UpdateNumber(String), -    UpdateExtension(String),      ToggleWork,      ToggleHome,      ToggleText, @@ -29,13 +32,19 @@ pub enum Msg {  #[derive(Clone, PartialEq, Properties)]  pub struct Props { -    pub generated: Callback<Telephone>, +    pub generated: Callback<Irc<Telephone>>,      pub weak_link: WeakComponentLink<TelephoneView>,  }  impl VCardPropertyInputComponent<Telephone> for TelephoneView {      fn get_input_object(&self) -> Telephone { -        self.value.clone() +        match self.value.irc().try_unwrap() { +            Ok(telephone) => telephone, +            Err(_) => { +                ConsoleService::error("Couldn't unwrap telephone"); +                Telephone::new() +            }, +        }      }      fn get_title(&self) -> String {          "Telephone".to_string() @@ -52,14 +61,13 @@ impl Component for TelephoneView {          props.weak_link.borrow_mut().replace(link);          Self {              props, -            value: Telephone::new(), +            value: Mrc::new(Telephone::new()),              error: None,          }      }      fn update(&mut self, msg: <Self as yew::Component>::Message) -> bool {          match msg {              Msg::UpdateNumber(n) => self.value.number = n, -            Msg::UpdateExtension(e) => self.value.extension = e,              Msg::ToggleWork => self.value.work = !self.value.work,              Msg::ToggleHome => self.value.home = !self.value.home,              Msg::ToggleText => self.value.text = !self.value.text, @@ -70,18 +78,13 @@ impl Component for TelephoneView {              Msg::TogglePager => self.value.pager = !self.value.pager,              Msg::ToggleTextPhone => self.value.text_phone = !self.value.text_phone,              Msg::Generate => { -                self.props.generated.emit(self.value); +                self.props.generated.emit(self.value.irc());              }          };          true      }      fn change(&mut self, props: <Self as yew::Component>::Properties) -> bool {  -        if self.props != props { -            self.props = props; -            true -        } else { -            false -        } +        self.props.neq_assign(props)      }      fn view(&self) -> yew::virtual_dom::VNode {          let link = self.props.weak_link.borrow().clone().unwrap(); diff --git a/src/viewmodel/address.rs b/src/viewmodel/address.rs index 61bd464..6fd3173 100644 --- a/src/viewmodel/address.rs +++ b/src/viewmodel/address.rs @@ -1,7 +1,7 @@  use super::*;  use crate::view::address::*; -#[derive(Clone)] +#[derive(Clone, Debug)]  pub struct Address {      pub post_office_box: String,      pub extension: String, diff --git a/src/viewmodel/mod.rs b/src/viewmodel/mod.rs index cc25d68..de433de 100644 --- a/src/viewmodel/mod.rs +++ b/src/viewmodel/mod.rs @@ -1,7 +1,7 @@  use yew::prelude::*; -use vobject::Property;  use crate::view::VCardPropertyInputComponent; +pub mod vcard;  pub mod address;  pub mod name;  pub mod telephone; diff --git a/src/viewmodel/name.rs b/src/viewmodel/name.rs index 26beaa1..a88e07f 100644 --- a/src/viewmodel/name.rs +++ b/src/viewmodel/name.rs @@ -1,7 +1,7 @@  use super::*;  use crate::view::name::*; -#[derive(Clone)] +#[derive(Clone, Debug)]  pub struct Name {      pub prefix: String,      pub first_name: String, @@ -70,7 +70,7 @@ impl VCardPropertyInputObject<NameView> for Name {  impl Name {      pub fn generate_fn(&self) -> String { -        let full_name = String::new(); +        let mut full_name = String::new();          full_name.push_str(&self.prefix);          if !self.first_name.is_empty() { diff --git a/src/viewmodel/telephone.rs b/src/viewmodel/telephone.rs index e5f63f0..6c930a2 100644 --- a/src/viewmodel/telephone.rs +++ b/src/viewmodel/telephone.rs @@ -1,11 +1,9 @@ -use std::collections::HashSet;  use super::*;  use crate::view::telephone::*; -#[derive(Clone)] +#[derive(Clone, Debug)]  pub struct Telephone {      pub number: String, -    pub extension: String,      pub work: bool,      pub home: bool,      pub text: bool, @@ -21,7 +19,6 @@ impl VCardPropertyInputObject<TelephoneView> for Telephone {      fn new() -> Self {          Self {              number: String::new(), -            extension: String::new(),              work: false,              home: false,              text: false, @@ -42,13 +39,6 @@ impl VCardPropertyInputObject<TelephoneView> for Telephone {                  oninput: link.callback(|e: InputData| Msg::UpdateNumber(e.value)),                  value: self.number.clone(),              }, -            VCardPropertyInputField::Text{ -                label: "Extension".to_string(), -                id: Some("extension".to_string()), -                placeholder: None, -                oninput: link.callback(|e: InputData| Msg::UpdateExtension(e.value)), -                value: self.extension.clone(), -            },              VCardPropertyInputField::CheckBox{                  label: "Work".to_string(),                  id: Some("work".to_string()), @@ -106,7 +96,6 @@ impl VCardPropertyInputObject<TelephoneView> for Telephone {          ]      }      fn is_empty(&self) -> bool { -        self.number.is_empty() && -        self.extension.is_empty() +        self.number.is_empty()      }  }
\ No newline at end of file diff --git a/src/viewmodel/vcard.rs b/src/viewmodel/vcard.rs new file mode 100644 index 0000000..2d415b7 --- /dev/null +++ b/src/viewmodel/vcard.rs @@ -0,0 +1,32 @@ +use yewtil::ptr::Irc; +use crate::viewmodel::telephone::Telephone; +use crate::viewmodel::address::Address; +use crate::viewmodel::name::Name; + +#[derive(Clone, Debug)] +pub struct VCardData { +    pub names: Vec<Irc<Name>>, +    pub addresses: Vec<Irc<Address>>, +    pub telephones: Vec<Irc<Telephone>>, +} + +macro_rules! make_vec_adder_fn { +    ( $fnname:ident $property:ident $($arg_name:ident : $arg_type:ty),* ) => { +        pub fn $fnname(&mut self, $( $arg_name : $arg_type ),*) { +            $(self.$property.push($arg_name);)* +        } +    }; +} + +impl VCardData { +    pub fn new() -> Self { +        Self { +            names: Vec::new(), +            addresses: Vec::new(), +            telephones: Vec::new(), +        } +    } +    make_vec_adder_fn!( add_name names name: Irc<Name> ); +    make_vec_adder_fn!( add_address addresses address: Irc<Address> ); +    make_vec_adder_fn!( add_telephone telephones telephone: Irc<Telephone> ); +}
\ No newline at end of file  | 
