Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Tarasikee committed Aug 7, 2022
1 parent beb3514 commit d1911ba
Show file tree
Hide file tree
Showing 23 changed files with 395 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
.env
.idea
database
9 changes: 9 additions & 0 deletions import_map.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"imports": {
"@core": "./src/mod.ts",
"@core/classes": "./src/classes/mod.ts",
"@core/decorators": "./src/decorators/mod.ts",
"@core/utils": "./src/utils/mod.ts",
"@core/errors": "./src/errors/mod.ts"
}
}
5 changes: 0 additions & 5 deletions import_maps.json

This file was deleted.

58 changes: 58 additions & 0 deletions src/Instance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import {Schema} from "./mod.ts"
import {FileUtils, ColumnsUtils} from "./utils/mod.ts"

interface InstanceOptions {
isNew: boolean;
}

export class Instance<T extends { _id: string }> {
constructor(private _schema: Schema,
private _fields: T,
private _options: InstanceOptions) {
}

set fields(value: T) {
this._fields = value
}

get fields(): T {
return this._fields
}

public delete() {
const db = FileUtils.readJson<T>("./database/db.json")
const filtered_table = db[this._schema.name]
.filter(row => row._id !== this._fields._id)
FileUtils.writeJson("./database/db.json", {
...db,
[this._schema.name]: filtered_table
})
}

public save() {
const db = FileUtils.readJson<T>("./database/db.json")

if (!this._options.isNew) {
const filtered_table = db[this._schema.name]
.filter(row => row._id !== this._fields._id)

new ColumnsUtils(this._schema.columns, filtered_table, this._fields)
filtered_table.push(this._fields)
FileUtils.writeJson("./database/db.json", {
...db,
[this._schema.name]: filtered_table
}
)
} else {
const table = db[this._schema.name] ?? []

new ColumnsUtils(this._schema.columns, table, this._fields)
table.push({...this._fields, _id: crypto.randomUUID()})
FileUtils.writeJson("./database/db.json", {
...db,
[this._schema.name]: table
}
)
}
}
}
52 changes: 52 additions & 0 deletions src/Model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {Schema, Instance} from "./mod.ts"
import {FileUtils, ObjectUtils} from "./utils/mod.ts"

export class Model<T extends { _id: string }> {
constructor(
private schema: Schema
) {
}

public create(args: T) {
return new Instance(this.schema, args, {isNew: true})
}

public findById(_id: string) {
const db = FileUtils.readJson<T>("./database/db.json")
const table = db[this.schema.name] ?? []
const candidate = table.find(row => row._id === _id)

if (candidate === undefined) {
throw new Error("No record found")
}

return new Instance<T>(this.schema, candidate, {
isNew: false
})
}

public find(args: Partial<T>) {
const db = FileUtils.readJson<T>("./database/db.json")
const table = db[this.schema.name] ?? []
const keys = Object.keys(args) as unknown as Array<keyof T>

const filtered_table = table.filter(row =>
keys.every(key =>
ObjectUtils.nestedCheck(row, key, args[key])))

return filtered_table.map(row => new Instance<T>(this.schema, row, {
isNew: false
}))
}

public findOne(args: Partial<T>) {
return this.find(args)[0]
}

public findAll() {
const db = FileUtils.readJson<T>("./database/db.json")
return db[this.schema.name].map(row => new Instance<T>(this.schema, row, {
isNew: false
}))
}
}
29 changes: 29 additions & 0 deletions src/Schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {getFormat} from "./decorators/mod.ts";
import {ColumnRules} from "./interfaces/IColumn.ts";

export class Schema {
constructor(
private _name: string = '',
private _columns: ColumnRules[] = []
) {
}

get name(): string {
return this._name;
}

get columns(): ColumnRules[] {
return this._columns;
}

public static initializeSchema<T extends { new(...args: unknown[]): unknown }>(args: T ) {
const instance = new (args as any)()

const keys = Object.keys(instance).filter(key => key !== "_table_name" && key !== "_id");
const options = keys.map(key => ({
name: key, options: getFormat(instance, key)
}))

return new Schema(instance["_table_name"], options);
}
}
28 changes: 28 additions & 0 deletions src/Table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {FileUtils} from "./utils/mod.ts";

export class Table {
public static async init(name: string) {
try {
const isExists = await FileUtils.isFileExists("./database/db.json");

if (isExists) {
return this;
}

await FileUtils.createOrCheckDir("./database");
await FileUtils.writeJson("./database/db.json", {[name]: []});
return this;
} catch (e) {
console.error(e.message);
}
}

public static async nuke(name: string) {
try {
await FileUtils.writeJson("./database/db.json", {[name]: []});
return this;
} catch (e) {
console.error(e.message);
}
}
}
4 changes: 4 additions & 0 deletions src/classes/Document.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export class Document {
_id!: string;
_table_name!: string;
}
1 change: 1 addition & 0 deletions src/classes/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {Document} from "./Document.ts";
16 changes: 16 additions & 0 deletions src/decorators/Column.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {Reflect} from "https://deno.land/x/[email protected]/mod.ts";
import {ColumnProps} from "../interfaces/IColumn.ts";

const formatMetadataKey = Symbol("columns");

export function getFormat(target: unknown, propertyKey: string) {
return Reflect.getMetadata(formatMetadataKey, target, propertyKey);
}

export function Column(options: ColumnProps) {
const optionsProxy = options.allowNull === undefined
? {...options, allowNull: false}
: {...options, allowNull: true};

return Reflect.metadata(formatMetadataKey, optionsProxy);
}
15 changes: 15 additions & 0 deletions src/decorators/TinyTable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {Table} from "../Table.ts";

export function TinyTable(name: string) {
return function<T extends { new(...args: any[]): Record<string, any> }>(Constructor: T) {
return class extends Constructor {
_table_name: string = name;
_id!: string;

constructor(...args: any[]) {
super(...args);
Table.init(name);
}
}
}
}
2 changes: 2 additions & 0 deletions src/decorators/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export {getFormat, Column} from "./Column.ts"
export {TinyTable} from "./TinyTable.ts"
8 changes: 8 additions & 0 deletions src/errors/ErrorWithHint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export class ErrorWithHint extends Error {
constructor(message: string, hint: string) {
super(`
Message: ${message}
Hint: ${hint}
`);
}
}
1 change: 1 addition & 0 deletions src/errors/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {ErrorWithHint} from "./ErrorWithHint.ts";
13 changes: 13 additions & 0 deletions src/interfaces/IColumn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export type OptionTypes = "string" | "number" | "boolean" | "date" | "json" | "array";

export interface ColumnProps {
unique?: boolean;
type?: OptionTypes;
allowNull?: boolean;
default?: any;
}

export interface ColumnRules {
name: string;
options: ColumnProps;
}
1 change: 1 addition & 0 deletions src/interfaces/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type {OptionTypes, ColumnRules, ColumnProps} from './IColumn.ts'
4 changes: 4 additions & 0 deletions src/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export {Model} from "./Model.ts";
export {Schema} from "./Schema.ts";
export {Table} from "./Table.ts";
export {Instance} from "./Instance.ts";
77 changes: 77 additions & 0 deletions src/utils/ColumnsUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import {ColumnRules, OptionTypes} from "../interfaces/mod.ts";
import {ErrorWithHint} from "../errors/mod.ts";
import {parse} from "https://deno.land/[email protected]/datetime/mod.ts";

export class ColumnsUtils<T> {
constructor(
private columnRules: ColumnRules[] = [],
private table: Array<T> = [],
private record: T
) {
this.run();
}

private checkType(value: T[keyof T], columnName: string, checkType?: OptionTypes) {
const valueType = typeof value;

if (checkType === "date") {
try {
return parse(String(value), "yyyy-MM-dd");
} catch (_) {
throw new Error(`${columnName} must be data`);
}
}

if (checkType === "array") {
if (!Array.isArray(value)) throw new Error(`${columnName} must be array`);
return;
}

if (checkType === "json") {
if (typeof value === "object" && !Array.isArray(value) && value !== null) return;
throw new Error(`${columnName} must be json`);
}

if (valueType !== checkType) {
throw new Error(`${columnName} must be ${checkType}`);
}
}

private unique(column: ColumnRules) {
const name = column.name as keyof T;

if (this.table.some(row => row[name] === this.record[name])) {
throw new ErrorWithHint(
`${String(name)} must be unique`,
`${this.record[name]} is already exists`
);
}
}

private type(column: ColumnRules) {
const name = column.name as keyof T;
const value = this.record[name];
return this.checkType(value, String(name), column.options.type);
}

private default(column: ColumnRules) {
const name = column.name as keyof T;
this.record[name] = column.options.default;
}

private run() {
this.columnRules.map(rule => {
if (rule.options.allowNull &&
this.record[rule.name as keyof T] === undefined &&
rule.options.default === undefined) return;


if (
rule.options.default !== undefined &&
this.record[rule.name as keyof T] === undefined) this.default(rule);

rule.options.type !== undefined && this.type(rule);
rule.options.unique !== undefined && this.unique(rule);
});
}
}
40 changes: 40 additions & 0 deletions src/utils/FileUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ensureDir } from "https://deno.land/[email protected]/fs/mod.ts";

export class FileUtils {
static writeJson<T>(path: string, data: Record<string, unknown> | Array<T | never>): string {
try {
Deno.writeTextFileSync(path, JSON.stringify(data));
return "Written to " + path;
} catch (e) {
return e.message;
}
}

static async isFileExists(path: string): Promise<boolean> {
try {
await Deno.stat(path);
return true;
} catch (e) {
console.log(e.message)
return false;
}
}

static async createOrCheckDir(path: string): Promise<string> {
try {
await ensureDir(path);
return "Created directory " + path;
} catch (e) {
return e.message;
}
}

static readJson<T>(path: string): Record<string, Array<T>> {
try {
const data = Deno.readTextFileSync(path);
return JSON.parse(data);
} catch (_e) {
return {};
}
}
}
Loading

0 comments on commit d1911ba

Please sign in to comment.