Add init command

This commit is contained in:
fluo10 2025-05-06 22:15:29 +09:00
parent cb83a386dc
commit ba84742a13
10 changed files with 151 additions and 93 deletions

6
Cargo.lock generated
View file

@ -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",

View file

@ -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
thiserror.workspace = true
tokio.workspace = true
toml.workspace = true

View file

@ -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<dpts_client::error::Error> 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),
}
}
}

72
dpts-cli/src/init.rs Normal file
View file

@ -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<Tz>,
}
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<Tz>,
}
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
}
}

View file

@ -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(),
}
}

View file

@ -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
toml.workspace = true

5
dpts-client/src/auth.rs Normal file
View file

@ -0,0 +1,5 @@
use crate::error::Error;
pub fn try_login(user_name: &str, password: &str, endpoint: &str) -> Result<String, Error> {
todo!()
}

View file

@ -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<ClientConfig> = 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<ClientConfig, Self::Error> {
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<ClientStorageConfig>,
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<PartialClientConfig> = 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<PartialClientConfig> = OnceCell::const_new();
static LOCAL_STORAGE_CONFIG_STRUCT: OnceCell<Config> = 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<PartialClientConfig> = OnceCell::const_new();
static REMOTE_STORAGE_CONFIG_STRUCT: OnceCell<Config> = 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);
}

View file

@ -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);
}
}

View file

@ -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<PartialGlobalConfig> 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))]