cardamum 0.1.0

CLI to manage contacts
// This file is part of Cardamum, a CLI to manage contacts.
//
// Copyright (C) 2025 soywod <clement.douin@posteo.net>
//
// This program is free software: you can redistribute it and/or
// modify it under the terms of the GNU Affero General Public License
// as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <https://www.gnu.org/licenses/>.

use std::collections::HashSet;

use anyhow::{anyhow, bail, Result};
use io_addressbook::{addressbook::Addressbook, card::Card};

use crate::account::Account;
#[cfg(feature = "carddav")]
use crate::carddav::client::CarddavClient;
#[cfg(feature = "vdir")]
use crate::vdir::client::VdirClient;

#[derive(Debug, Default)]
pub enum Client<'a> {
    #[default]
    None,
    #[cfg(feature = "carddav")]
    Carddav(CarddavClient<'a>),
    #[cfg(feature = "vdir")]
    Vdir(VdirClient),
}

impl<'a> Client<'a> {
    pub fn new(account: &'a Account) -> Result<Self> {
        #[cfg(feature = "carddav")]
        if let Some(config) = &account.carddav {
            return Ok(Self::Carddav(CarddavClient::new(config)?));
        }

        #[cfg(feature = "vdir")]
        if let Some(config) = &account.vdir {
            return Ok(Self::Vdir(VdirClient::new(config)));
        }

        Err(anyhow!("Cannot find CardDAV nor Vdir config")
            .context("Create addressbook client error"))
    }

    pub fn create_addressbook(&mut self, addressbook: Addressbook) -> Result<()> {
        match self {
            Self::None => bail!("Missing addressbook backend"),
            #[cfg(feature = "carddav")]
            Self::Carddav(client) => client.create_addressbook(addressbook),
            #[cfg(feature = "vdir")]
            Self::Vdir(client) => client.create_addressbook(addressbook),
        }
    }

    pub fn list_addressbooks(&mut self) -> Result<HashSet<Addressbook>> {
        match self {
            Self::None => bail!("Missing addressbook backend"),
            #[cfg(feature = "carddav")]
            Self::Carddav(client) => client.list_addressbooks(),
            #[cfg(feature = "vdir")]
            Self::Vdir(client) => client.list_addressbooks(),
        }
    }

    pub fn list_cards(&mut self, addressbook_id: impl AsRef<str>) -> Result<HashSet<Card>> {
        match self {
            #[cfg(feature = "carddav")]
            Self::Carddav(client) => client.list_cards(addressbook_id),
            #[cfg(feature = "vdir")]
            Self::Vdir(client) => client.list_cards(addressbook_id),
            Self::None => bail!("client not defined"),
        }
    }

    pub fn update_addressbook(&mut self, addressbook: Addressbook) -> Result<()> {
        match self {
            Self::None => bail!("Missing addressbook backend"),
            #[cfg(feature = "carddav")]
            Self::Carddav(client) => client.update_addressbook(addressbook),
            #[cfg(feature = "vdir")]
            Self::Vdir(client) => client.update_addressbook(addressbook),
        }
    }

    pub fn delete_addressbook(&mut self, id: impl AsRef<str>) -> Result<()> {
        match self {
            Self::None => bail!("Missing addressbook backend"),
            #[cfg(feature = "carddav")]
            Self::Carddav(client) => client.delete_addressbook(id),
            #[cfg(feature = "vdir")]
            Self::Vdir(client) => client.delete_addressbook(id),
        }
    }

    pub fn create_card(&mut self, card: Card) -> Result<()> {
        match self {
            Self::None => bail!("Missing addressbook backend"),
            #[cfg(feature = "carddav")]
            Self::Carddav(client) => client.create_card(card),
            #[cfg(feature = "vdir")]
            Self::Vdir(client) => client.create_card(card),
        }
    }

    pub fn read_card(
        &mut self,
        addressbook_id: impl AsRef<str>,
        card_id: impl AsRef<str>,
    ) -> Result<Card> {
        match self {
            Self::None => bail!("Missing addressbook backend"),
            #[cfg(feature = "carddav")]
            Self::Carddav(client) => client.read_card(addressbook_id, card_id),
            #[cfg(feature = "vdir")]
            Self::Vdir(client) => client.read_card(addressbook_id, card_id),
        }
    }

    pub fn update_card(&mut self, card: Card) -> Result<()> {
        match self {
            Self::None => bail!("Missing addressbook backend"),
            #[cfg(feature = "carddav")]
            Self::Carddav(client) => client.update_card(card),
            #[cfg(feature = "vdir")]
            Self::Vdir(client) => client.update_card(card),
        }
    }

    pub fn delete_card(
        &mut self,
        addressbook_id: impl AsRef<str>,
        card_id: impl AsRef<str>,
    ) -> Result<()> {
        match self {
            Self::None => bail!("Missing addressbook backend"),
            #[cfg(feature = "carddav")]
            Self::Carddav(client) => client.delete_card(addressbook_id, card_id),
            #[cfg(feature = "vdir")]
            Self::Vdir(client) => client.delete_card(addressbook_id, card_id),
        }
    }
}