-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added global capture and capture enum #45
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,7 +16,7 @@ | |
# the CodeChat Editor. If not, see | ||
# [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). | ||
# | ||
# # `Cargo.toml` -- Rust build/package management config for the server | ||
# # `Cargo.toml` -- Rust build/package management config | ||
# | ||
# ## General package configurations | ||
[package] | ||
|
@@ -29,59 +29,70 @@ license = "GPL-3.0-only" | |
name = "codechat-editor-server" | ||
readme = "../README.md" | ||
repository = "https://github.com/bjones1/CodeChat_Editor" | ||
version = "0.1.5" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Revert this change. |
||
version = "0.1.3" | ||
|
||
# This library allows other packages to use core CodeChat Editor features. | ||
[lib] | ||
name = "code_chat_editor" | ||
|
||
# ## Dependencies | ||
[dependencies] | ||
actix = "0.13.1" | ||
async-once-cell = "0.5.0" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would you install cargo sort then run it? I think most of these changes are due to sorted vs. non-sorted dependencies. |
||
actix-files = "0.6" | ||
actix-rt = "2.9.0" | ||
actix-web = "4" | ||
actix-ws = "0.3.0" | ||
bytes = { version = "1", features = ["serde"] } | ||
chrono = "0.4" | ||
clap = { version = "4.5.19", features = ["derive"] } | ||
dunce = "1.0.5" | ||
futures-util = "0.3.29" | ||
indoc = "2.0.5" | ||
lazy_static = "1" | ||
log = "0.4" | ||
log4rs = "1.3" | ||
mime = "0.3.17" | ||
mime_guess = "2.0.5" | ||
minreq = "2.12.0" | ||
normalize-line-endings = "0.3.0" | ||
notify-debouncer-full = "0.4" | ||
open = "5.3.0" | ||
path-slash = "0.2.1" | ||
pest = "2.7.14" | ||
pest_derive = "2.7.14" | ||
# Per the [docs](https://docs.rs/crate/pulldown-cmark/latest), skip building the | ||
# binary. | ||
pulldown-cmark = { version = "0.12", default-features = false, features = ["html"] } | ||
regex = "1" | ||
serde = { version = "1", features = ["derive"] } | ||
serde_json = "1" | ||
tokio = { version = "1", features = ["full"] } | ||
tokio-postgres = { version = "0.7", features = ["with-chrono-0_4"] } | ||
url = "2.5.2" | ||
urlencoding = "2" | ||
# Per the [docs](https://docs.rs/crate/pulldown-cmark/latest), skip building the | ||
# binary. | ||
pulldown-cmark = { version = "0.12", default-features = false, features = ["html"] } | ||
futures-util = "0.3.29" | ||
async-trait = "0.1.81" | ||
open = "5.3.0" | ||
dunce = "1.0.5" | ||
minreq = "2.12.0" | ||
mime = "0.3.17" | ||
mime_guess = "2.0.5" | ||
url = "2.5.2" | ||
clap = { version = "4.5.19", features = ["derive"] } | ||
indoc = "2.0.5" | ||
cmd_lib = "1.9.5" | ||
tokio-postgres = { version = "0.7", features = ["with-chrono-0_4"] } | ||
simplelog = "0.12.0" | ||
config = "0.13" | ||
pest = "2.7.14" | ||
pest_derive = "2.7.14" | ||
normalize-line-endings = "0.3.0" | ||
uuid = { version = "1", features = ["v4"] } | ||
dirs = "5.0.1" | ||
|
||
# [Windows-only dependencies](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#platform-specific-dependencies). | ||
[target.'cfg(windows)'.dependencies] | ||
win_partitions = "0.3.0" | ||
|
||
# ### Development-only dependencies | ||
[dev-dependencies] | ||
assertables = "9" | ||
assert_fs = "1" | ||
tokio-tungstenite = "0.24" | ||
actix-http = "3.9.0" | ||
assert_cmd = "2.0.16" | ||
assert_fs = "1" | ||
assertables = "9" | ||
predicates = "3.1.2" | ||
tokio-tungstenite = "0.26" | ||
# See the [docs](https://github.com/rust-lang/rust-clippy#usage) to install | ||
# clippy; it can't be installed as a dev-dependency. See the | ||
# [fmt docs](https://github.com/rust-lang/rustfmt#quick-start) to install fmt. | ||
|
||
# #### Use local packages for development | ||
[patch.crates-io] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,37 +21,103 @@ | |
// Standard library | ||
use indoc::indoc; | ||
use std::fs; | ||
use std::io; | ||
use std::io::{self, Write}; | ||
use std::path::Path; | ||
use std::path::PathBuf; | ||
use std::sync::Arc; | ||
|
||
// Third-party | ||
use async_once_cell::OnceCell; | ||
use chrono::Local; | ||
use dirs::config_dir; | ||
use log::{error, info}; | ||
use serde::{Deserialize, Serialize}; | ||
use tokio::sync::Mutex; | ||
use tokio_postgres::{Client, NoTls}; | ||
use uuid::Uuid; | ||
|
||
pub static GLOBAL_EVENT_CAPTURE: OnceCell<Arc<EventCapture>> = OnceCell::new(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove |
||
|
||
pub async fn get_event_capture() -> Arc<EventCapture> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice find! I like this! |
||
GLOBAL_EVENT_CAPTURE | ||
.get_or_init(async { | ||
let capture = EventCapture::new("config.json") | ||
.await | ||
.expect("Failed to initialize EventCapture"); | ||
Arc::new(capture) | ||
}) | ||
.await | ||
.clone() | ||
} | ||
|
||
// Local | ||
|
||
/// Returns the path where the identifier file is stored. | ||
fn get_uuid_file_path() -> Result<PathBuf, io::Error> { | ||
let mut config_path = config_dir().ok_or_else(|| { | ||
io::Error::new( | ||
io::ErrorKind::NotFound, | ||
"Could not find configuration directory", | ||
) | ||
})?; | ||
config_path.push("vsc_extension"); | ||
config_path.push("uuid.txt"); | ||
Ok(config_path) | ||
} | ||
|
||
/// Creates a new UUID and writes it to the file if it does not already exist. | ||
fn create_uuid() -> Result<(), io::Error> { | ||
let path = self::get_uuid_file_path()?; | ||
|
||
// Check if the file already exists | ||
if path.exists() { | ||
return Ok(()); // UUID already exists, no need to create | ||
} | ||
|
||
// Generate a new UUID | ||
let new_uuid = Uuid::new_v4().to_string(); | ||
|
||
// Ensure the parent directory exists | ||
if let Some(parent) = path.parent() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrong logic -- only runs if if !path.parent().expect("Config directory has no parent").exists()`. |
||
fs::create_dir_all(parent)?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add some context into errors returned. For example, fs::create_dir_all(parent).map_err(|err| {
io::Error::new(
err.kind(),
format!("Unable to create config directory {path:?}: {err}"),
)
})?; |
||
} | ||
|
||
// Write the new UUID to the file | ||
let mut file = fs::File::create(&path)?; | ||
file.write_all(new_uuid.as_bytes())?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above -- add context to the error. |
||
Ok(()) | ||
} | ||
|
||
/// Retrieves the UUID from the file. If the file does not exist, it creates one. | ||
pub fn get_uuid() -> Result<String, io::Error> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this be non- |
||
let path = get_uuid_file_path()?; | ||
|
||
// If the file does not exist, create it | ||
if !path.exists() { | ||
self::create_uuid()?; | ||
} | ||
|
||
// Read and return the UUID | ||
let uuid = fs::read_to_string(&path)?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add context to error. |
||
Ok(uuid.trim().to_string()) | ||
} | ||
|
||
/* ## The Event Structure: | ||
|
||
The `Event` struct represents an event to be stored in the database. | ||
|
||
Fields: - `user_id`: The ID of the user associated with the event. - | ||
Fields: - | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please check formatting in CodeChat -- it's messed up here and elsewhere. |
||
`event_type`: The type of event (e.g., "keystroke", "file_open"). - `data`: | ||
Optional additional data associated with the event. | ||
|
||
### Example | ||
|
||
let event = Event { user_id: "user123".to_string(), event_type: | ||
"keystroke".to_string(), data: Some("Pressed key A".to_string()), }; | ||
let event = Event {event_type:"keystroke".to_string(), data: Some("Pressed key A".to_string()), }; | ||
*/ | ||
|
||
#[derive(Deserialize, Debug)] | ||
pub struct Event { | ||
pub user_id: String, | ||
pub event_type: String, | ||
pub event_type: EventType, // Use the EventType enum | ||
pub data: Option<String>, | ||
} | ||
|
||
|
@@ -95,7 +161,6 @@ holds a `tokio_postgres::Client` for database operations. | |
|
||
// Create an event | ||
let event = Event { | ||
user_id: "user123".to_string(), | ||
event_type: "keystroke".to_string(), | ||
data: Some("Pressed key A".to_string()), | ||
}; | ||
|
@@ -111,6 +176,31 @@ pub struct EventCapture { | |
db_client: Arc<Mutex<Client>>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps make this a |
||
} | ||
|
||
// Define a globally available EventType enum | ||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
pub enum EventType { | ||
KeyStroke, | ||
FileOpen, | ||
FileSave, | ||
ApplicationStart, | ||
ApplicationExit, | ||
System, | ||
} | ||
|
||
impl EventType { | ||
/// Convert the EventType enum to a string representation | ||
pub fn as_str(&self) -> &'static str { | ||
match self { | ||
EventType::KeyStroke => "KeyStroke", | ||
EventType::FileOpen => "FileOpen", | ||
EventType::FileSave => "FileSave", | ||
EventType::ApplicationStart => "ApplicationStart", | ||
EventType::ApplicationExit => "ApplicationExit", | ||
EventType::System => "System Event", | ||
} | ||
} | ||
} | ||
|
||
/* | ||
## The EventCapture Implementation | ||
*/ | ||
|
@@ -181,7 +271,6 @@ impl EventCapture { | |
let event_capture = EventCapture::new("config.json").await?; | ||
|
||
let event = Event { | ||
user_id: "user123".to_string(), | ||
event_type: "keystroke".to_string(), | ||
data: Some("Pressed key A".to_string()), | ||
}; | ||
|
@@ -201,6 +290,15 @@ impl EventCapture { | |
VALUES ($1, $2, $3, $4) | ||
"}; | ||
|
||
// Retrieve the UUID, creating it if necessary | ||
let uuid = match get_uuid() { | ||
Ok(uuid) => uuid, | ||
Err(err) => { | ||
error!("Error obtaining UUID: {:?}", err); | ||
String::new() // Provide a fallback value, e.g., an empty string | ||
} | ||
}; | ||
|
||
// Acquire a lock on the database client for thread-safe access | ||
let client = self.db_client.lock().await; | ||
|
||
|
@@ -209,8 +307,8 @@ impl EventCapture { | |
.execute( | ||
stmt, | ||
&[ | ||
&event.user_id, | ||
&event.event_type, | ||
&uuid, | ||
&event.event_type.as_str(), | ||
&formatted_time, | ||
&event.data, | ||
], | ||
|
@@ -232,7 +330,7 @@ CREATE TABLE events ( id SERIAL PRIMARY KEY, user_id TEXT NOT NULL, | |
event_type TEXT NOT NULL, timestamp TEXT NOT NULL, data TEXT ); | ||
|
||
- **`id SERIAL PRIMARY KEY`**: Auto-incrementing primary key. | ||
- **`user_id TEXT NOT NULL`**: The ID of the user associated with the event. | ||
- **`user_id TEXT NOT NULL`**: The ID of the user associated with the event (UUID). | ||
- **`event_type TEXT NOT NULL`**: The type of event. | ||
- **`timestamp TEXT NOT NULL`**: The timestamp of the event. | ||
- **`data TEXT`**: Optional additional data associated with the event. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -71,7 +71,10 @@ use vscode::{ | |
}; | ||
|
||
// ### Local | ||
//use crate::capture::EventCapture; | ||
use crate::capture::get_event_capture; | ||
use crate::capture::Event; | ||
use crate::capture::EventCapture; | ||
use crate::capture::EventType; | ||
use crate::processing::{ | ||
source_to_codechat_for_web_string, CodeChatForWeb, TranslationResultsString, | ||
}; | ||
|
@@ -80,10 +83,10 @@ use filewatcher::{ | |
filewatcher_websocket, | ||
}; | ||
|
||
// ## Data structures | ||
// | ||
// ### Data structures supporting a websocket connection between the IDE, this server, and the CodeChat Editor Client | ||
// | ||
/// ## Data structures | ||
/// | ||
/// ### Data structures supporting a websocket connection between the IDE, this server, and the CodeChat Editor Client | ||
/// | ||
/// Provide queues which send data to the IDE and the CodeChat Editor Client. | ||
#[derive(Debug)] | ||
struct WebsocketQueues { | ||
|
@@ -318,9 +321,6 @@ lazy_static! { | |
static ref ROOT_PATH: PathBuf = { | ||
let exe_path = env::current_exe().unwrap(); | ||
let exe_dir = exe_path.parent().unwrap(); | ||
#[cfg(not(any(test, debug_assertions)))] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keep these changes. |
||
let root_path = PathBuf::from(exe_dir); | ||
#[cfg(any(test, debug_assertions))] | ||
let mut root_path = PathBuf::from(exe_dir); | ||
// When in debug or running tests, use the layout of the Git repo to | ||
// find client files. In release mode, we assume the static folder is a | ||
|
@@ -1077,12 +1077,22 @@ async fn client_websocket( | |
// ## Webserver core | ||
#[actix_web::main] | ||
pub async fn main(port: u16) -> std::io::Result<()> { | ||
// Initialize EventCapture | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put this in |
||
let event_capture = get_event_capture().await; | ||
|
||
// Example usage | ||
let event = Event { | ||
event_type: EventType::System, | ||
data: Some("Server Started".to_string()), | ||
}; | ||
|
||
event_capture.insert_event(event).await?; | ||
run_server(port).await | ||
} | ||
|
||
pub async fn run_server(port: u16) -> std::io::Result<()> { | ||
// Connect to the Capture Database | ||
//let _event_capture = EventCapture::new("config.json").await?; | ||
let _event_capture = EventCapture::new("config.json").await?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Delete this, I think -- I assume the code above will replace this? |
||
|
||
// Pre-load the bundled files before starting the webserver. | ||
let _ = &*BUNDLED_FILES_MAP; | ||
|
@@ -1104,9 +1114,6 @@ pub async fn run_server(port: u16) -> std::io::Result<()> { | |
} | ||
|
||
pub fn configure_logger(level: LevelFilter) { | ||
#[cfg(not(debug_assertions))] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keep these changes -- you removed them. |
||
let l4rs = ROOT_PATH.clone(); | ||
#[cfg(debug_assertions)] | ||
let mut l4rs = ROOT_PATH.clone(); | ||
#[cfg(debug_assertions)] | ||
l4rs.push("server"); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Revert this change.