Skip to content

Commit 6899ae2

Browse files
committed
0.4.1
1 parent 9889e28 commit 6899ae2

6 files changed

Lines changed: 161 additions & 92 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
edition = "2021"
33
name = "deltafy_logos-dev-cli-utils"
4-
version = "0.4.0"
4+
version = "0.4.1"
55

66
[lib]
77
crate-type = ["cdylib"]

index.d.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33

44
/* auto-generated by NAPI-RS */
55

6+
export interface PgResponse {
7+
code: string
8+
message: string
9+
}
610
export interface ProcessOutput {
711
status: number
812
stdout: string
913
stderr: string
1014
}
11-
export interface PgResponse {
12-
code: string
13-
message: string
14-
}
1515
export declare function runNpmScript(script: string): Promise<ProcessOutput>
1616
export declare function testPostgresUrl(url: string): Promise<PgResponse>
1717
export declare function createDatabase(url: string, database: string): Promise<PgResponse>

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@deltafy/logos-dev-cli-utils",
3-
"version": "0.4.0",
3+
"version": "0.4.1",
44
"main": "index.js",
55
"types": "index.d.ts",
66
"repository": "https://github.com/deltafy/logos-dev-cli-utils",

src/file.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use std::fs::{self, File};
2+
3+
pub fn read_file(path: &str) -> napi::Result<File> {
4+
fs::File::open(path)
5+
.map_err(|error| napi::Error::new(
6+
napi::Status::GenericFailure,
7+
format!("Failed to open {}: {}", path, error)
8+
))
9+
}
10+
11+
pub fn create_file(path: &str) -> napi::Result<File> {
12+
fs::File::create(path)
13+
.map_err(|error| napi::Error::new(
14+
napi::Status::GenericFailure,
15+
format!("Failed to create {}: {}", path, error)
16+
))
17+
}
18+
19+
#[cfg(test)]
20+
mod tests {
21+
use super::*;
22+
use std::fs;
23+
use std::io::Write;
24+
use std::path::Path;
25+
26+
fn setup_file(path: &str, contents: &str) {
27+
let mut file = fs::File::create(path).expect("Failed to create test file");
28+
file.write_all(contents.as_bytes()).expect("Failed to write to test file");
29+
}
30+
31+
fn cleanup_file(path: &str) {
32+
if Path::new(path).exists() {
33+
fs::remove_file(path).expect("Failed to remove test file");
34+
}
35+
}
36+
37+
#[test]
38+
fn test_read_file_success() {
39+
let filename = "test_read_success.txt";
40+
let data = "Test";
41+
setup_file(filename, data);
42+
43+
let result = read_file(filename);
44+
assert!(result.is_ok(), "Successful file read expected");
45+
46+
let file = result.unwrap();
47+
assert_eq!(file.metadata().unwrap().len(), 4, "File size should match");
48+
49+
cleanup_file(filename);
50+
}
51+
52+
#[test]
53+
fn test_read_file_failed() {
54+
let filename = "test_read_failed.txt";
55+
let result = read_file(filename);
56+
57+
assert!(result.is_err(), "Failed file read expected");
58+
}
59+
60+
#[test]
61+
fn test_create_file_success() {
62+
let filename = "test_create_success.txt";
63+
let result = create_file(filename);
64+
65+
assert!(result.is_ok(), "Successful file creation expected");
66+
67+
let file = result.unwrap();
68+
assert!(file.metadata().is_ok(), "File should exist");
69+
70+
cleanup_file(filename);
71+
}
72+
73+
#[test]
74+
fn test_create_file_failed() {
75+
let filename = "some_dir/test_create_failed.txt";
76+
let result = create_file(filename);
77+
78+
assert!(result.is_err(), "Failed file read to nonexistent subdirectory expected");
79+
}
80+
}

src/lib.rs

Lines changed: 21 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
use napi_derive::napi;
22
use std::env;
3-
use std::fs::{self, File};
3+
use std::fs;
44
use std::io::{self, Write};
55
use std::path::Path;
66
use std::process::Command;
77
use serde::{Serialize, Deserialize};
8-
use tokio_postgres::{NoTls, Error as PgError, Client as PgClient, Connection as PgConnection};
8+
9+
mod postgres;
10+
mod file;
11+
12+
use postgres::PgResponse;
913

1014
// map std::process::Output so we could mimic the callback
1115
// parameters of Node's child_process.exec()
@@ -17,75 +21,6 @@ pub struct ProcessOutput {
1721
pub stderr: String, // convert from Vec<u8>
1822
}
1923

20-
#[napi(object)]
21-
#[derive(Serialize, Deserialize)]
22-
pub struct PgResponse {
23-
pub code: String, // convert from SqlState
24-
pub message: String,
25-
}
26-
27-
fn get_postgres_error_message(error: &PgError) -> String {
28-
if let Some(db_error) = error.as_db_error() {
29-
db_error.message().to_string()
30-
} else {
31-
error.to_string()
32-
}
33-
}
34-
35-
fn create_postgres_error_response(error: &PgError) -> napi::Result<PgResponse> {
36-
let (error_code, error_details) = if let Some(db_error) = error.as_db_error() {
37-
(
38-
db_error.code().code().to_string(),
39-
db_error.message().to_string()
40-
)
41-
} else {
42-
(
43-
"unknown".to_string(),
44-
error.to_string()
45-
)
46-
};
47-
48-
let error_result = PgResponse {
49-
code: error_code,
50-
message: error_details
51-
};
52-
53-
Err(napi::Error::new(
54-
napi::Status::GenericFailure,
55-
serde_json::to_string(&error_result).unwrap()
56-
))
57-
}
58-
59-
async fn create_postgres_connection(
60-
url: &str
61-
) -> Result<(PgClient, PgConnection<tokio_postgres::Socket, tokio_postgres::tls::NoTlsStream>), PgError> {
62-
tokio_postgres::connect(url, NoTls).await
63-
}
64-
65-
async fn handle_postgres_connection(
66-
conn: PgConnection<tokio_postgres::Socket, tokio_postgres::tls::NoTlsStream>
67-
) {
68-
if let Err(error) = conn.await {
69-
eprintln!("Connection Error: {}", get_postgres_error_message(&error))
70-
}
71-
}
72-
73-
fn read_file(path: &str) -> napi::Result<File> {
74-
fs::File::open(path)
75-
.map_err(|error| napi::Error::new(
76-
napi::Status::GenericFailure,
77-
format!("Failed to open {}: {}", path, error)
78-
))
79-
}
80-
81-
fn create_file(path: &str) -> napi::Result<File> {
82-
fs::File::create(path)
83-
.map_err(|error| napi::Error::new(
84-
napi::Status::GenericFailure,
85-
format!("Failed to create {}: {}", path, error)
86-
))
87-
}
88-
8924
#[napi]
9025
pub async fn run_npm_script(script: String) -> napi::Result<ProcessOutput> {
9126
let home_dir = env::var("HOME").or_else(|_| env::var("USERPROFILE")).unwrap_or_default();
@@ -121,42 +56,42 @@ pub async fn run_npm_script(script: String) -> napi::Result<ProcessOutput> {
12156

12257
#[napi]
12358
pub async fn test_postgres_url(url: String) -> napi::Result<PgResponse> {
124-
let (client, connection) = match create_postgres_connection(&url).await {
59+
let (client, connection) = match postgres::create_postgres_connection(&url).await {
12560
Ok((client, connection)) => (client, connection),
12661
Err(error) => {
127-
return create_postgres_error_response(&error);
62+
return postgres::create_postgres_error_response(&error);
12863
}
12964
};
13065

131-
tokio::spawn(handle_postgres_connection(connection));
66+
tokio::spawn(postgres::handle_postgres_connection(connection));
13267

13368
match client.simple_query("SELECT 1").await {
13469
Ok(_) => Ok(PgResponse {
13570
code: "00000".to_string(),
13671
message: "Success".to_string()
13772
}),
138-
Err(error) => create_postgres_error_response(&error),
73+
Err(error) => postgres::create_postgres_error_response(&error),
13974
}
14075
}
14176

14277
#[napi]
14378
pub async fn create_database(url: String, database: String) -> napi::Result<PgResponse> {
144-
let (client, connection) = match create_postgres_connection(&url).await {
79+
let (client, connection) = match postgres::create_postgres_connection(&url).await {
14580
Ok((client, connection)) => (client, connection),
14681
Err(error) => {
147-
return create_postgres_error_response(&error);
82+
return postgres::create_postgres_error_response(&error);
14883
}
14984
};
15085

151-
tokio::spawn(handle_postgres_connection(connection));
86+
tokio::spawn(postgres::handle_postgres_connection(connection));
15287

15388
let query = format!("CREATE DATABASE \"{}\"", database);
15489
match client.simple_query(&query).await {
15590
Ok(_) => Ok(PgResponse {
15691
code: "00000".to_string(),
15792
message: format!("Successfully created database '{}'", database)
15893
}),
159-
Err(error) => create_postgres_error_response(&error)
94+
Err(error) => postgres::create_postgres_error_response(&error)
16095
}
16196
}
16297

@@ -213,22 +148,22 @@ pub fn file_exists(file_path: String) -> bool {
213148

214149
#[napi]
215150
pub async fn rename_database(url: String, database: String, new_database_name: String) -> napi::Result<PgResponse> {
216-
let (client, connection) = match create_postgres_connection(&url).await {
151+
let (client, connection) = match postgres::create_postgres_connection(&url).await {
217152
Ok((client, connection)) => (client, connection),
218153
Err(error) => {
219-
return create_postgres_error_response(&error);
154+
return postgres::create_postgres_error_response(&error);
220155
}
221156
};
222157

223-
tokio::spawn(handle_postgres_connection(connection));
158+
tokio::spawn(postgres::handle_postgres_connection(connection));
224159

225160
let query = format!("ALTER DATABASE \"{}\" RENAME TO \"{}\";", database, new_database_name);
226161
match client.batch_execute(&query).await {
227162
Ok(_) => Ok(PgResponse {
228163
code: "00000".to_string(),
229164
message: format!("Database {} renamed to {}", database, new_database_name)
230165
}),
231-
Err(error) => create_postgres_error_response(&error)
166+
Err(error) => postgres::create_postgres_error_response(&error)
232167
}
233168
}
234169

@@ -246,12 +181,12 @@ pub fn copy_file(
246181
destination: String,
247182
create_dest_if_not_exists: Option<bool>
248183
) -> napi::Result<()> {
249-
let mut source_file = read_file(&source)?;
184+
let mut source_file = file::read_file(&source)?;
250185

251186
let mut destination_file = if create_dest_if_not_exists.unwrap_or(false) {
252-
create_file(&destination)?
187+
file::create_file(&destination)?
253188
} else {
254-
read_file(&destination)?
189+
file::read_file(&destination)?
255190
};
256191

257192
io::copy(&mut source_file, &mut destination_file)

src/postgres.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use napi_derive::napi;
2+
use serde::{Serialize, Deserialize};
3+
use tokio_postgres::{Client, Error, Connection, NoTls, Socket};
4+
use tokio_postgres::tls::NoTlsStream;
5+
6+
#[napi(object)]
7+
#[derive(Serialize, Deserialize)]
8+
pub struct PgResponse {
9+
pub code: String, // convert from SqlState
10+
pub message: String,
11+
}
12+
13+
pub fn get_postgres_error_message(error: &Error) -> String {
14+
if let Some(db_error) = error.as_db_error() {
15+
db_error.message().to_string()
16+
} else {
17+
error.to_string()
18+
}
19+
}
20+
21+
pub fn create_postgres_error_response(error: &Error) -> napi::Result<PgResponse> {
22+
let (error_code, error_details) = if let Some(db_error) = error.as_db_error() {
23+
(
24+
db_error.code().code().to_string(),
25+
db_error.message().to_string()
26+
)
27+
} else {
28+
(
29+
"unknown".to_string(),
30+
error.to_string()
31+
)
32+
};
33+
34+
let error_result = PgResponse {
35+
code: error_code,
36+
message: error_details
37+
};
38+
39+
Err(napi::Error::new(
40+
napi::Status::GenericFailure,
41+
serde_json::to_string(&error_result).unwrap()
42+
))
43+
}
44+
45+
pub async fn create_postgres_connection(url: &str) -> Result<(Client, Connection<Socket, NoTlsStream>), Error> {
46+
tokio_postgres::connect(url, NoTls).await
47+
}
48+
49+
pub async fn handle_postgres_connection(conn: Connection<Socket, NoTlsStream>) {
50+
if let Err(error) = conn.await {
51+
eprintln!("Connection Error: {}", get_postgres_error_message(&error))
52+
}
53+
}
54+

0 commit comments

Comments
 (0)