From 4221a4f0567b6ddf0fdf49d35e7ecc7d30919879 Mon Sep 17 00:00:00 2001 From: JenChieh Date: Sun, 9 Jun 2024 18:59:57 -0700 Subject: [PATCH] feat: Add property capability --- Cargo.toml | 1 + src/main.rs | 62 ++++++++++++++++--------- src/{server.rs => server/mod.rs} | 1 + src/server/properties.rs | 77 ++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 20 deletions(-) rename src/{server.rs => server/mod.rs} (99%) create mode 100644 src/server/properties.rs diff --git a/Cargo.toml b/Cargo.toml index 8e33c36..68dfead 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ chrono = "0.4.38" clap = "4.5.4" dunce = "1.0.4" ignore = "0.4.22" +java-properties = "2.0.0" path-slash = "0.2.1" rpassword = "7.3.1" serde = { version = "1.0.203", features = ["derive"] } diff --git a/src/main.rs b/src/main.rs index 3a81a08..30b033a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,28 +33,34 @@ use clap::{arg, Arg, ArgMatches, Command}; use dunce; use fmt::Layer; use rpassword; +use server::properties::Properties; use server::Server; -use std::env::current_dir; use std::io; use std::io::Write; +use std::str::FromStr; use tracing::Level; use tracing_appender::non_blocking::WorkerGuard; use tracing_subscriber::{fmt, layer::SubscriberExt}; const DOT_COGRU: &str = "./.cogru"; +const PROP_FILE: &str = "./Cogru.properties"; -const LOG_LEVEL: Level = Level::DEBUG; // Default is `DEBUG` +const DEFAULT_HOST: &str = "127.0.0.1"; +const DEFAULT_PORT: &str = "8786"; /// Setup logger rotator. /// /// https://docs.rs/tracing-appender/0.2.3/tracing_appender/non_blocking/struct.WorkerGuard.html -pub fn setup_logger() -> WorkerGuard { +pub fn setup_logger(prop: &Properties) -> WorkerGuard { println!("Setup logger :::"); let file_appender = tracing_appender::rolling::hourly(DOT_COGRU, "example.log"); let (non_blocking, guard) = tracing_appender::non_blocking(file_appender); + let prop_log_level = prop.get_or_default("cogru.LogLevel", "DEBUG"); + let level = Level::from_str(&prop_log_level).unwrap(); + let subscriber = tracing_subscriber::fmt() - .with_max_level(LOG_LEVEL) + .with_max_level(level) .finish() .with(Layer::new().with_writer(non_blocking)); @@ -68,12 +74,14 @@ pub fn setup_logger() -> WorkerGuard { /// /// * `port` - port to start. /// * `password` - password to enter the session. -async fn start_server(port: u16, working_dir: &str, password: Option) { - let _guard = setup_logger(); - +async fn start_server(prop: &Properties, port: u16, working_dir: &str, password: Option) { println!("Start room server :::"); + let host = prop.get_or_default("cogru.Host", DEFAULT_HOST); + + println!("host: {}", host); + let room = Room::new(working_dir, password); - let mut server = Server::new("127.0.0.1", port, room); + let mut server = Server::new(&host, port, room); let _ = server.start().await; } @@ -105,10 +113,9 @@ fn get_workspace(matches: &ArgMatches) -> String { .to_string() } -/// Program Entry -#[tokio::main] -async fn main() { - let matches = Command::new("Cogru") +/// Setup CLI. +fn setup_cli() -> ArgMatches { + Command::new("Cogru") .version("0.1.0") .about("cogru - Where the collaboration start!?") .arg( @@ -121,7 +128,7 @@ async fn main() { arg!(--port ) .required(false) .help("Port number") - .default_value("8786"), + .default_value(DEFAULT_PORT), ) .arg( Arg::new("no_password") @@ -132,7 +139,16 @@ async fn main() { .help("Don't require password to enter the room") .default_value("false"), ) - .get_matches(); + .get_matches() +} + +/// Program Entry +#[tokio::main] +async fn main() { + let prop = Properties::new(&PROP_FILE); + let prop_port = prop.get_or_default("cogru.Port", DEFAULT_PORT); + + let matches = setup_cli(); let current_dir = get_workspace(&matches); let mut current_dir = to_slash(¤t_dir); @@ -141,11 +157,14 @@ async fn main() { current_dir = format!("{}/", current_dir); } - let port = matches - .get_one::("port") - .unwrap() - .parse::() - .unwrap(); + let mut port = matches.get_one::("port").unwrap(); + + if port == DEFAULT_PORT { + port = &prop_port; + } + + // Convert to u16 + let port = port.parse::().unwrap(); let no_password = matches.get_flag("no_password"); @@ -155,6 +174,9 @@ async fn main() { Some(get_password().expect("Confirm password doesn't match")) }; + // Setup logger + let _guard = setup_logger(&prop); + // Start the server - start_server(port, ¤t_dir, password).await; + start_server(&prop, port, ¤t_dir, password).await; } diff --git a/src/server.rs b/src/server/mod.rs similarity index 99% rename from src/server.rs rename to src/server/mod.rs index fefd067..acd0cc8 100644 --- a/src/server.rs +++ b/src/server/mod.rs @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +pub mod properties; use crate::channel::*; use crate::client::*; use crate::connection::*; diff --git a/src/server/properties.rs b/src/server/properties.rs new file mode 100644 index 0000000..1d69705 --- /dev/null +++ b/src/server/properties.rs @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2024 Cogru Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +use java_properties::read; +use java_properties::write; +use java_properties::PropertiesIter; +use java_properties::PropertiesWriter; +use std::collections::HashMap; +use std::fs::File; +use std::io::BufReader; +use std::net::ToSocketAddrs; +use std::path::Path; + +pub struct Properties { + data: HashMap, + read: bool, +} + +impl Properties { + pub fn new(path: &str) -> Self { + let mut prop = Self { + data: HashMap::new(), + read: false, + }; + prop.read(path); + prop + } + + fn read(&mut self, path: &str) { + if !Path::new(path).exists() { + return; + } + + let f = File::open(path).unwrap(); + + let _ = PropertiesIter::new(BufReader::new(f)).read_into(|k, v| { + self.data.insert(k, v); + }); + + self.read = true; + } + + /// Return property value. + /// + /// # Arguments + /// + /// * `key` - The key used to find value. + pub fn get(&self, key: &str) -> Option<&String> { + self.data.get(key) + } + + /// Return property value or the default value when null. + /// + /// # Arguments + /// + /// * `key` - The key used to find value. + /// * `default_value` - The fallback value. + pub fn get_or_default(&self, key: &str, default_value: &str) -> String { + let data = self.get(key); + if data.is_none() { + return default_value.to_string(); + } + data.unwrap().clone() + } +}