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
// Copyright 2023-2024 Hugo Osvaldo Barrera
//
// SPDX-License-Identifier: EUPL-1.2

use clap::{Args, Parser, Subcommand, ValueEnum};
use http::Uri;

use crate::{caldav, carddav};

#[derive(Clone, ValueEnum)]
enum Verbosity {
    Error,
    Warn,
    Info,
    Debug,
    Trace,
}

#[derive(Args)]
pub(crate) struct Server {
    /// A base URL from which to discover the server.
    ///
    /// Examples: `http://localhost:8080`, `https://example.com`.
    #[arg(long)]
    pub(crate) server_url: Uri,
}

#[derive(Args)]
#[group(required = true, multiple = false)]
struct Proto {
    /// Operate on a CalDav server.
    #[arg(long)]
    caldav: bool,

    /// Operate on a CardDav server.
    #[arg(long)]
    carddav: bool,
}

#[derive(Parser)]
#[clap(author, version = env!("DAVCLI_VERSION"), about, long_about = None)]
pub(crate) struct Cli {
    #[command(flatten)]
    proto: Proto,

    #[command(subcommand)]
    pub(crate) command: ServerCommand,

    /// Change logging verbosity
    ///
    /// Logging is always directed to `stderr`.
    #[clap(short, long)]
    verbose: Option<Verbosity>,
}

#[derive(Subcommand)]
pub(crate) enum ServerCommand {
    /// Perform discovery and print results
    Discover,
    /// Find collections under the home set.
    FindCollections,
    /// List items in a given collection.
    ListItems { collection_href: String },
    /// List all collections and items recursively.
    Tree,
    /// Fetches a single item.
    Get { resource_href: String },
    /// Create a new item.
    ///
    /// Data is read from stdin.
    Create { resource_href: String },
    /// Delete an item or collection.
    Delete {
        #[arg(long)]
        force: bool,
        href: String,
    },
}

impl Cli {
    pub(crate) fn execute(self) -> anyhow::Result<()> {
        assert_ne!(self.proto.carddav, self.proto.caldav);
        if self.proto.caldav {
            caldav::execute(self.command)
        } else {
            carddav::execute(self.command)
        }
    }

    /// Returns the desired log level based on the amount of `-v` flags.
    /// The default log level is WARN.
    pub(crate) fn log_level(&self) -> log::Level {
        match self.verbose {
            Some(Verbosity::Error) => log::Level::Error,
            Some(Verbosity::Warn) | None => log::Level::Warn,
            Some(Verbosity::Info) => log::Level::Info,
            Some(Verbosity::Debug) => log::Level::Debug,
            Some(Verbosity::Trace) => log::Level::Trace,
        }
    }
}

#[test]
fn verify_cli() {
    use clap::CommandFactory;
    Cli::command().debug_assert();
}