diff --git a/Cargo.lock b/Cargo.lock index f4fbc97..7056365 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -838,15 +838,19 @@ name = "dpts-cli" version = "0.1.0" dependencies = [ "chrono", + "chrono-tz", "clap", - "dpts-core", + "dpts-client", "thiserror 2.0.12", + "tokio", + "toml", ] [[package]] name = "dpts-client" version = "0.1.0" dependencies = [ + "chrono-tz", "clap", "dpts-core", "serde", diff --git a/dpts-cli/Cargo.toml b/dpts-cli/Cargo.toml index 4ee3080..a1454d5 100644 --- a/dpts-cli/Cargo.toml +++ b/dpts-cli/Cargo.toml @@ -6,7 +6,10 @@ edition.workspace = true repository.workspace = true [dependencies] -dpts-core = {workspace = true} +dpts-client = {workspace = true} chrono = {workspace = true} +chrono-tz.workspace = true clap = {workspace = true, features = ["derive"]} -thiserror.workspace = true \ No newline at end of file +thiserror.workspace = true +tokio.workspace = true +toml.workspace = true \ No newline at end of file diff --git a/dpts-cli/src/error.rs b/dpts-cli/src/error.rs index 01c2014..c834783 100644 --- a/dpts-cli/src/error.rs +++ b/dpts-cli/src/error.rs @@ -3,6 +3,18 @@ pub enum Error { #[error("Parse int error")] ParseInt(#[from] std::num::ParseIntError), #[error("Missing config value: ({0})")] - MissingConfig(String) + MissingConfig(String), + #[error("Parse toml error")] + TomlDe(#[from] toml::de::Error), } + +impl From for Error { + fn from(e: dpts_client::error::Error) -> Self { + match e { + dpts_client::error::Error::TomlDe(x)=> Self::TomlDe(x), + dpts_client::error::Error::ParseInt(x) => Self::ParseInt(x), + dpts_client::error::Error::MissingConfig(x) => Self::MissingConfig(x), + } + } +} diff --git a/dpts-cli/src/init.rs b/dpts-cli/src/init.rs new file mode 100644 index 0000000..1bb50fe --- /dev/null +++ b/dpts-cli/src/init.rs @@ -0,0 +1,72 @@ +use clap::{Args, Subcommand}; +use chrono_tz::Tz; +use crate::error::Error; +use dpts_client::auth::try_login; +use dpts_client::config::{ + Config, + ClientConfig, + ClientRemoteStorageConfig, + ClientStorageConfig, + PartialGlobalConfig +}; + +#[derive(Args, Clone, Debug)] +pub struct InitArgs { + #[command(subcommand)] + pub command: InitCommand, +} + +impl InitArgs { + pub fn run(self) -> Result<(), Error> { + match self.command { + InitCommand::Local(x) => x.run(), + InitCommand::Remote(x) => x.run(), + } + } +} +#[derive(Clone, Debug, Subcommand)] + +enum InitCommand { + Local(InitLocalArgs), + Remote(InitRemoteArgs), +} + +#[derive(Args, Clone, Debug)] +pub struct InitLocalArgs { + pub time_zone: Option, +} + +impl InitLocalArgs { + pub fn run(self) -> Result<(), Error> { + unimplemented!() + } +} + +#[derive(Args, Clone, Debug)] +pub struct InitRemoteArgs { + pub endpoint: String, + #[arg(short, long)] + pub user_name: String, + #[arg(short, long)] + pub password: String, + #[arg(short, long)] + pub time_zone: Option, +} + +impl InitRemoteArgs { + pub fn run(self) -> Result<(), Error> { + let token: String = try_login(&self.user_name, &self.password, &self.endpoint)?; + let config: Config = Config{ + global: PartialGlobalConfig { + time_zone: self.time_zone.clone() + }, + client: ClientConfig { + storage: ClientStorageConfig::Remote(ClientRemoteStorageConfig { + endpoint: self.endpoint, + access_key: token, + }), + } + }; + todo!() // Write config + } +} diff --git a/dpts-cli/src/main.rs b/dpts-cli/src/main.rs index 9d55476..aafd4ba 100644 --- a/dpts-cli/src/main.rs +++ b/dpts-cli/src/main.rs @@ -1,13 +1,15 @@ //mod label; pub mod error; +mod init; mod record; + //use label::LabelArgs; use record::{RecordArgs,RecordAddArgs}; use error::Error; - +use init::InitArgs; use clap::{Args, CommandFactory, Parser, Subcommand}; use std::ffi::OsString; @@ -22,18 +24,18 @@ struct Cli { #[derive(Clone, Debug, Subcommand)] enum Command { + Init(InitArgs), Record(RecordArgs), } - -fn main() -> Result<(), Error> { +#[tokio::main] +async fn main() -> Result<(), Error> { let cli = Cli::parse(); match cli.command { //Some(Commands::Add(x)) => x.run(), + Command::Init(x) => x.run(), //Some(Commands::Label(x)) => x.run(), Command::Record(x) => x.run(), } - - } diff --git a/dpts-client/Cargo.toml b/dpts-client/Cargo.toml index 966eb3c..d2e4e34 100644 --- a/dpts-client/Cargo.toml +++ b/dpts-client/Cargo.toml @@ -8,9 +8,10 @@ default = ["clap"] clap = ["dep:clap"] [dependencies] +chrono-tz.workspace = true clap = { workspace = true, optional = true } dpts-core.workspace = true serde.workspace = true thiserror.workspace = true tokio.workspace = true -toml.workspace = true \ No newline at end of file +toml.workspace = true diff --git a/dpts-client/src/auth.rs b/dpts-client/src/auth.rs new file mode 100644 index 0000000..c3dd17b --- /dev/null +++ b/dpts-client/src/auth.rs @@ -0,0 +1,5 @@ +use crate::error::Error; + +pub fn try_login(user_name: &str, password: &str, endpoint: &str) -> Result { + todo!() +} \ No newline at end of file diff --git a/dpts-client/src/config/mod.rs b/dpts-client/src/config/mod.rs index 2e5f1c0..9c22367 100644 --- a/dpts-client/src/config/mod.rs +++ b/dpts-client/src/config/mod.rs @@ -1,7 +1,10 @@ mod storage; +pub use dpts_core::config::*; pub use storage::*; + + use crate::error::Error; use serde::{ Deserialize, @@ -12,84 +15,45 @@ use tokio::sync::OnceCell; pub static CLIENT_CONFIG: OnceCell = OnceCell::const_new(); +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] - -pub struct ClientConfig { - pub storage: ClientStorageConfig, -} - -impl TryFrom<&PartialClientConfig> for ClientConfig { - type Error = Error; - fn try_from(p: &PartialClientConfig) -> Result { - Ok(ClientConfig{ - storage: p.clone().storage.ok_or(Error::MissingConfig("storage".to_string()))?, - }) - - } +pub struct Config { + pub client: ClientConfig, + pub global: PartialGlobalConfig, } #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] -pub struct PartialClientConfig { - pub storage: Option, +pub struct ClientConfig { + pub storage: ClientStorageConfig, } -impl PartialClientConfig { - -} - - -impl Default for PartialClientConfig { - fn default() -> Self { - PartialClientConfig { - storage: None - } - } -} - - #[cfg(test)] mod tests { use super::*; + use chrono_tz::UTC; use tokio::sync::OnceCell; - const EMPTY_CONFIG_TOML: &str = r#""#; - static EMPTY_CONFIG_STRUCT: OnceCell = OnceCell::const_new(); - - async fn get_empty_config_struct() -> &'static PartialClientConfig { - EMPTY_CONFIG_STRUCT.get_or_init(|| async { - PartialClientConfig{ - storage: None, - } - }).await - } - - #[tokio::test] - async fn deserialize_empty_client_config() { - let config: PartialClientConfig = toml::from_str(EMPTY_CONFIG_TOML).unwrap(); - assert_eq!(&config, get_empty_config_struct().await); - } - - #[tokio::test] - async fn serialize_empty_client_config() { - assert_eq!(EMPTY_CONFIG_TOML, toml::to_string(get_empty_config_struct().await).unwrap()); - } - - const LOCAL_STORAGE_CONFIG_TOML: &str = r#"time_zone = "UTC" + const LOCAL_STORAGE_CONFIG_TOML: &str = r#"[client] storage = "local" + +[global] "#; - static LOCAL_STORAGE_CONFIG_STRUCT: OnceCell = OnceCell::const_new(); + static LOCAL_STORAGE_CONFIG_STRUCT: OnceCell = OnceCell::const_new(); - async fn get_local_storage_client_config_struct() -> &'static PartialClientConfig { + async fn get_local_storage_client_config_struct() -> &'static Config { LOCAL_STORAGE_CONFIG_STRUCT.get_or_init(|| async { - PartialClientConfig{ - storage: Some(ClientStorageConfig::Local), + Config{ + client: ClientConfig{ + storage: ClientStorageConfig::Local, + }, + global: PartialGlobalConfig { time_zone: None }, } }).await } #[tokio::test] async fn deserialize_local_storage_client_config() { - let config: PartialClientConfig = toml::from_str(LOCAL_STORAGE_CONFIG_TOML).unwrap(); + let config: Config = toml::from_str(LOCAL_STORAGE_CONFIG_TOML).unwrap(); assert_eq!(&config, get_local_storage_client_config_struct().await); } @@ -98,27 +62,31 @@ storage = "local" assert_eq!(LOCAL_STORAGE_CONFIG_TOML, toml::to_string(get_local_storage_client_config_struct().await).unwrap()); } - const REMOTE_STORAGE_CONFIG_TOML: &str = r#"time_zone = "UTC" - -[storage.remote] + const REMOTE_STORAGE_CONFIG_TOML: &str = r#"[client.storage.remote] endpoint = "https://example.com" access_key = "test" + +[global] +time_zone = "UTC" "#; - static REMOTE_STORAGE_CONFIG_STRUCT: OnceCell = OnceCell::const_new(); + static REMOTE_STORAGE_CONFIG_STRUCT: OnceCell = OnceCell::const_new(); - async fn get_remote_storage_client_config_struct() -> &'static PartialClientConfig { + async fn get_remote_storage_client_config_struct() -> &'static Config { REMOTE_STORAGE_CONFIG_STRUCT.get_or_init(|| async { - PartialClientConfig{ - storage: Some(ClientStorageConfig::Remote(ClientRemoteStorageConfig { - endpoint: "https://example.com".to_string(), - access_key: "test".to_string(), - })), + Config{ + client: ClientConfig { + storage: ClientStorageConfig::Remote(ClientRemoteStorageConfig { + endpoint: "https://example.com".to_string(), + access_key: "test".to_string(), + }) + }, + global: PartialGlobalConfig { time_zone: Some(UTC) } } }).await } #[tokio::test] async fn deserialize_remote_storage_client_config() { - let config: PartialClientConfig = toml::from_str(REMOTE_STORAGE_CONFIG_TOML).unwrap(); + let config: Config = toml::from_str(REMOTE_STORAGE_CONFIG_TOML).unwrap(); assert_eq!(&config, get_remote_storage_client_config_struct().await); } diff --git a/dpts-client/src/lib.rs b/dpts-client/src/lib.rs index b4c4449..db8ca9f 100644 --- a/dpts-client/src/lib.rs +++ b/dpts-client/src/lib.rs @@ -1,17 +1,8 @@ +pub mod auth; pub mod config; pub mod error; -pub fn add(left: u64, right: u64) -> u64 { - left + right -} +use error::Error; + -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} diff --git a/dpts-core/src/config/global.rs b/dpts-core/src/config/global.rs index fe921f2..9378664 100644 --- a/dpts-core/src/config/global.rs +++ b/dpts-core/src/config/global.rs @@ -1,13 +1,13 @@ use chrono_tz::Tz; #[cfg(feature="clap")] use clap::Args; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use tokio::sync::OnceCell; use crate::Error; -#[derive(Clone, Debug, Deserialize, PartialEq)] +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct GlobalConfig { pub time_zone: Tz, } @@ -21,7 +21,7 @@ impl TryFrom for GlobalConfig{ } } -#[derive(Clone, Debug, Default, Deserialize, PartialEq)] +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[cfg_attr(feature="clap", derive(Args))] pub struct PartialGlobalConfig { #[cfg_attr(feature="clap", arg(short, long))]