diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9ef5a18 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +target +index.node +**/node_modules +**/.DS_Store +Cargo.lock +npm-debug.log* diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..bbed4ce --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "db-csv-faker" +version = "0.1.0" +description = "tiny native extendable npm package for mocking db .csvs" +authors = ["zkud"] +license = "MIT" +edition = "2018" +exclude = ["index.node"] + +[dependencies] +uuid = { version = "0.8", features = ["v4"] } +rand = "0.8.4" + +[lib] +crate-type = ["cdylib"] + +[dependencies.neon] +version = "0.8" diff --git a/README.md b/README.md new file mode 100644 index 0000000..f116a48 --- /dev/null +++ b/README.md @@ -0,0 +1,107 @@ +# db-csv-faker + +**db-csv-faker:** tiny native extendable npm package for mocking db .csvs + +This project was bootstrapped by [create-neon](https://www.npmjs.com/package/create-neon). + +## Installing db-csv-faker + +Installing db-csv-faker requires a [supported version of Node and Rust](https://github.com/neon-bindings/neon#platform-support). + +You can install the project with npm. In the project directory, run: + +```sh +$ npm install +``` + +This fully installs the project, including installing any dependencies and running the build. + +## Building db-csv-faker + +If you have already installed the project and only want to run the build, run: + +```sh +$ npm run build +``` + +This command uses the [cargo-cp-artifact](https://github.com/neon-bindings/cargo-cp-artifact) utility to run the Rust build and copy the built library into `./index.node`. + +## Exploring db-csv-faker + +After building db-csv-faker, you can explore its exports at the Node REPL: + +```sh +$ npm install +$ node +> require('.').hello() +"hello node" +``` + +## Available Scripts + +In the project directory, you can run: + +### `npm install` + +Installs the project, including running `npm run build`. + +### `npm build` + +Builds the Node addon (`index.node`) from source. + +### `npm test` + +Runs the unit tests by calling `cargo test`. You can learn more about [adding tests to your Rust code](https://doc.rust-lang.org/book/ch11-01-writing-tests.html) from the [Rust book](https://doc.rust-lang.org/book/). + +## Project Layout + +The directory structure of this project is: + +``` +db-csv-faker/ +├── Cargo.toml +├── README.md +├── index.node +├── package.json +├── src/ +| └── lib.rs +└── target/ +``` + +### Cargo.toml + +The Cargo [manifest file](https://doc.rust-lang.org/cargo/reference/manifest.html), which informs the `cargo` command. + +### README.md + +This file. + +### index.node + +The Node addon—i.e., a binary Node module—generated by building the project. This is the main module for this package, as dictated by the `"main"` key in `package.json`. + +Under the hood, a [Node addon](https://nodejs.org/api/addons.html) is a [dynamically-linked shared object](https://en.wikipedia.org/wiki/Library_(computing)#Shared_libraries). The `"build"` script produces this file by copying it from within the `target/` directory, which is where the Rust build produces the shared object. + +### package.json + +The npm [manifest file](https://docs.npmjs.com/cli/v7/configuring-npm/package-json), which informs the `npm` command. + +### src/ + +The directory tree containing the Rust source code for the project. + +### src/lib.rs + +The Rust library's main module. + +### target/ + +Binary artifacts generated by the Rust build. + +## Learn More + +To learn more about Neon, see the [Neon documentation](https://neon-bindings.com). + +To learn more about Rust, see the [Rust documentation](https://www.rust-lang.org). + +To learn more about Node, see the [Node documentation](https://nodejs.org). diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..366641f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,14 @@ +{ + "name": "db-csv-faker", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "cargo-cp-artifact": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.4.tgz", + "integrity": "sha512-34yUas8aUENHGdk6JhLkV4ol0GLtP78YgqpsRDmmnpADy9JoTg/DgKM3CRHAeozTRNhKoPaRFhV+BxoqkmoKUA==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..2910ba5 --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "db-csv-faker", + "version": "0.1.0", + "description": "tiny native extendable npm package for mocking db .csvs", + "main": "index.node", + "scripts": { + "build": "cargo-cp-artifact -nc index.node -- cargo build --message-format=json-render-diagnostics", + "install": "npm run build", + "test": "cargo test" + }, + "author": "zkud", + "license": "MIT", + "devDependencies": { + "cargo-cp-artifact": "^0.1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/rapid-d9t/db-csv-faker.git" + }, + "keywords": [ + "csv" + ], + "bugs": { + "url": "https://github.com/rapid-d9t/db-csv-faker/issues" + }, + "homepage": "https://github.com/rapid-d9t/db-csv-faker#readme" +} diff --git a/src/dumpers/mod.rs b/src/dumpers/mod.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/dumpers/mod.rs @@ -0,0 +1 @@ + diff --git a/src/fields/field_error.rs b/src/fields/field_error.rs new file mode 100644 index 0000000..067993e --- /dev/null +++ b/src/fields/field_error.rs @@ -0,0 +1,77 @@ +use std::fmt; +use std::io; + +#[derive(fmt::Debug)] +pub struct FieldError { + error_type: FieldErrorType, + message: String, +} + +#[derive(fmt::Debug, Clone, PartialEq)] +pub enum FieldErrorType { + InvalidInputArgs, +} + +impl fmt::Display for FieldErrorType { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + FieldErrorType::InvalidInputArgs => write!(formatter, "InvalidInputArgs"), + } + } +} + +impl FieldError { + pub fn new(message: String, error_type: FieldErrorType) -> FieldError { + FieldError { + message, + error_type, + } + } + + pub fn get_message(&self) -> String { + self.message.clone() + } + + pub fn get_error_type(&self) -> FieldErrorType { + self.error_type.clone() + } +} + +impl fmt::Display for FieldError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + formatter, + "Field Error with type: {}, reason: {}", + self.error_type, self.message + ) + } +} + +#[cfg(test)] +mod tests { + use super::FieldError; + use super::FieldErrorType; + + #[test] + fn it_inits() { + let invalid_args_error = + FieldError::new("field error".to_string(), FieldErrorType::InvalidInputArgs); + + assert_eq!(invalid_args_error.get_message(), "field error"); + assert_eq!( + invalid_args_error.get_error_type(), + FieldErrorType::InvalidInputArgs + ); + } + + #[test] + fn it_displayable() { + let invalid_args_error = + FieldError::new("field error".to_string(), FieldErrorType::InvalidInputArgs); + + assert_eq!( + format!("{}", invalid_args_error), + "Field Error reason: InvalidInputArgs, reason: file error" + ); + } +} diff --git a/src/fields/integer_32_field.rs b/src/fields/integer_32_field.rs new file mode 100644 index 0000000..e00b3e1 --- /dev/null +++ b/src/fields/integer_32_field.rs @@ -0,0 +1,55 @@ +use neon::declare_types; +use neon::prelude::*; +use rand; +use rand::Rng; + +pub struct Integer32Field { + foreign_key_to: Option, +} + +declare_types! { + pub class JsInteger32Field for Integer32Field { + init(mut cx) { + let properties = cx.argument::(0)?; + + if let Ok(name) = properties.get(&mut cx, "foreignKeyTo") { + if let Ok(name) = name.downcast::() { + return Ok( + Integer32Field { + foreign_key_to: Some(name.value()) + } + ); + } + } + + Ok(Integer32Field { + foreign_key_to: None + }) + } + + method format(mut cx) { + let value = { + let this = cx.this(); + let guard = cx.lock(); + let fields = this.borrow(&guard); + let mut generator = rand::thread_rng(); + let value: i32 = generator.gen(); + format!("{}", value) + }; + Ok(cx.string(value).upcast()) + } + + method isForeignKeyTo(mut cx) { + let foreign_key_to = { + let this = cx.this(); + let guard = cx.lock(); + let fields = this.borrow(&guard); + fields.foreign_key_to.clone() + }; + match foreign_key_to { + Some(name) => Ok(cx.string(name).upcast()), + None => Ok(cx.undefined().upcast()) + } + } + } +} diff --git a/src/fields/mod.rs b/src/fields/mod.rs new file mode 100644 index 0000000..b074d58 --- /dev/null +++ b/src/fields/mod.rs @@ -0,0 +1,4 @@ +mod field_error; +pub mod integer_32_field; +pub mod string_field; +pub mod uuid_v4_field; diff --git a/src/fields/string_field.rs b/src/fields/string_field.rs new file mode 100644 index 0000000..5d317e3 --- /dev/null +++ b/src/fields/string_field.rs @@ -0,0 +1,58 @@ +use neon::declare_types; +use neon::prelude::*; +use rand; +use rand::distributions::Alphanumeric; +use rand::Rng; + +pub struct StringField { + foreign_key_to: Option, +} + +declare_types! { + pub class JsStringField for StringField { + init(mut cx) { + let properties = cx.argument::(0)?; + + if let Ok(name) = properties.get(&mut cx, "foreignKeyTo") { + if let Ok(name) = name.downcast::() { + return Ok( + StringField { + foreign_key_to: Some(name.value()) + } + ); + } + } + + Ok(StringField { + foreign_key_to: None + }) + } + + method format(mut cx) { + let value: String = { + let this = cx.this(); + let guard = cx.lock(); + let fields = this.borrow(&guard); + rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(30) + .map(char::from) + .collect() + }; + Ok(cx.string(value).upcast()) + } + + method isForeignKeyTo(mut cx) { + let foreign_key_to = { + let this = cx.this(); + let guard = cx.lock(); + let fields = this.borrow(&guard); + fields.foreign_key_to.clone() + }; + match foreign_key_to { + Some(name) => Ok(cx.string(name).upcast()), + None => Ok(cx.undefined().upcast()) + } + } + } +} diff --git a/src/fields/uuid_v4_field.rs b/src/fields/uuid_v4_field.rs new file mode 100644 index 0000000..0ecf6a0 --- /dev/null +++ b/src/fields/uuid_v4_field.rs @@ -0,0 +1,52 @@ +use neon::declare_types; +use neon::prelude::*; +use uuid::Uuid; + +pub struct UUIDV4Field { + foreign_key_to: Option, +} + +declare_types! { + pub class JsUUIDV4Field for UUIDV4Field { + init(mut cx) { + let properties = cx.argument::(0)?; + + if let Ok(name) = properties.get(&mut cx, "foreignKeyTo") { + if let Ok(name) = name.downcast::() { + return Ok( + UUIDV4Field { + foreign_key_to: Some(name.value()) + } + ); + } + } + + Ok(UUIDV4Field { + foreign_key_to: None + }) + } + + method format(mut cx) { + let value = { + let this = cx.this(); + let guard = cx.lock(); + let fields = this.borrow(&guard); + Uuid::new_v4().to_string() + }; + Ok(cx.string(value).upcast()) + } + + method isForeignKeyTo(mut cx) { + let foreign_key_to = { + let this = cx.this(); + let guard = cx.lock(); + let fields = this.borrow(&guard); + fields.foreign_key_to.clone() + }; + match foreign_key_to { + Some(name) => Ok(cx.string(name).upcast()), + None => Ok(cx.undefined().upcast()) + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..103bb8b --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,15 @@ +pub mod dumpers; +pub mod fields; +pub mod tables; + +use fields::integer_32_field::JsInteger32Field; +use fields::string_field::JsStringField; +use fields::uuid_v4_field::JsUUIDV4Field; +use neon::register_module; + +register_module!(mut m, { + m.export_class::("UUIDV4Field")?; + m.export_class::("Integer32Field")?; + m.export_class::("StringField")?; + Ok(()) +}); diff --git a/src/tables/mod.rs b/src/tables/mod.rs new file mode 100644 index 0000000..e69de29