Untitled

 avatar
unknown
typescript
10 months ago
3.1 kB
14
Indexable
export type Operator = "eq" | "neq" | "gt" | "gte" | "lt" | "lte";
export type Kind = "condition" | "group" | "not";
export type GroupType = "and" | "or";
export type Value = string | number | boolean | null;
export abstract class Expression {
  abstract kind: Kind;
}

export class Conditon extends Expression {
  kind: Kind = "condition";

  constructor(
    public column: string,
    public operator: Operator,
    public value: Value
  ) {
    super();
  }
}

export class Group extends Expression {
  kind: Kind = "group";

  constructor(public groupType: GroupType, public expressions: Expression[]) {
    super();
  }
}

export class Not extends Expression {
  kind: Kind = "not";
  constructor(public expr: Expression) {
    super();
  }
}

export const col = (column: string) => ({
  eq: (value: Value) => new Conditon(column, "eq", value),
  neq: (value: Value) => new Conditon(column, "neq", value),
  gt: (value: Value) => new Conditon(column, "gt", value),
  gte: (value: Value) => new Conditon(column, "gte", value),
  lt: (value: Value) => new Conditon(column, "lt", value),
  lte: (value: Value) => new Conditon(column, "lte", value),
});

export const and = (...expressions: Expression[]) =>
  new Group("and", expressions);
export const or = (...expressions: Expression[]) =>
  new Group("or", expressions);
export const not = (expr: Expression) => new Not(expr);

export const serialize = (value: unknown): string => {
  if (Array.isArray(value)) {
    return `(${value.map((v) => serialize(v)).join(", ")})`;
  }
  if (value === null) {
    return "NULL";
  }
  if (typeof value === "boolean") {
    return value ? "TRUE" : "FALSE";
  }
  if (typeof value === "number") {
    return value.toString();
  }
  if (typeof value === "string") {
    return `'${value.replace(/'/g, "''")}'`;
  }
  throw new Error(`Cannot serialize value: ${value}`);
};

export const serializeOperator = (operator: Operator): string => {
  switch (operator) {
    case "eq":
      return "=";
    case "neq":
      return "<>";
    case "gt":
      return ">";
    case "gte":
      return ">=";
    case "lt":
      return "<";
    case "lte":
      return "<=";
  }
  throw new Error(`Cannot serialize operator: ${operator}`);
};

export const emitCondition = (condition: Conditon): string => {
  return `${condition.column} ${serializeOperator(
    condition.operator
  )} ${serialize(condition.value)}`;
};

export const emitGroup = (group: Group): string => {
  return `(${group.expressions
    .map((expr) => emit(expr))
    .join(` ${group.groupType.toUpperCase()} `)})`;
};

export const emitNot = (not: Not): string => {
  return `(NOT ${emit(not.expr)})`;
};

export const emit = (expr: Expression): string => {
  switch (expr.kind) {
    case "condition":
      return emitCondition(expr as Conditon);
    case "group":
      return emitGroup(expr as Group);
    case "not":
      return emitNot(expr as Not);
  }
  throw new Error(`Cannot emit expression: ${expr}`);
};
Editor is loading...
Leave a Comment