diff --git a/bin/peptfilter.ts b/bin/peptfilter.ts new file mode 100755 index 00000000..007dfdba --- /dev/null +++ b/bin/peptfilter.ts @@ -0,0 +1,6 @@ +#!/usr/bin/env node + +import { Peptfilter } from '../lib/commands/peptfilter.js'; + +const command = new Peptfilter(); +command.run(); diff --git a/lib/commands/peptfilter.ts b/lib/commands/peptfilter.ts new file mode 100644 index 00000000..997801f2 --- /dev/null +++ b/lib/commands/peptfilter.ts @@ -0,0 +1,53 @@ +import { Option } from 'commander'; +import { createInterface } from 'node:readline'; +import { BaseCommand } from './base_command.js'; + +export class Peptfilter extends BaseCommand { + + readonly description = `The peptfilter command filters a list of peptides according to specific criteria. The command expects a list of peptides that are passed to standard input. + +The input should have one peptide per line. FASTA headers are preserved in the output, so that peptides remain bundled.`; + + constructor(options?: { exitOverride?: boolean, suppressOutput?: boolean, args?: string[] }) { + super(options); + + this.program + .summary("Filter peptides based on specific criteria.") + .description(this.description) + .option("--minlen ", "only retain peptides having at least this many amino acids", (d) => parseInt(d, 10), 5) + .option("--maxlen ", "only retain peptides having at most this many amino acids", (d) => parseInt(d, 10), 50) + .option("-l, --lacks ", "only retain peptides that lack all of the specified amino acids", (d) => d.split("")) + .option("-c, --contains ", "only retain peptides that contain all of the specified amino acids", (d) => d.split("")); + } + + async run() { + this.parseArguments(); + console.log(this.program.opts()) + const minLen = this.program.opts().minLen; + const maxlen = this.program.opts().maxLen; + const lacks = this.program.opts().lacks || []; + const contains = this.program.opts().contains || []; + + for await (const line of createInterface({ input: process.stdin })) { + if (line.startsWith(">")) { + process.stdout.write(line + "\n"); + continue; + } + if (Peptfilter.checkLength(line, minLen, maxlen) && Peptfilter.checkLacks(line, lacks) && Peptfilter.checkContains(line, contains)) { + process.stdout.write(line + "\n"); + } + } + } + + static checkLength(line: string, minLen: number, maxlen: number): boolean { + return line.length >= minLen && line.length <= maxlen; + } + + static checkLacks(line: string, lacks: string[]): boolean { + return lacks.every((aa: string) => !line.includes(aa)); + } + + static checkContains(line: string, contains: string[]): boolean { + return contains.every((aa: string) => line.includes(aa)); + } +} diff --git a/package.json b/package.json index 71171b87..7daaa389 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "private": false, "type": "module", "bin": { + "peptfilter": "./bin/peptfilter.js", "uniprot": "./bin/uniprot.js" }, "scripts": { @@ -15,6 +16,7 @@ "lint": "yarn run eslint", "test": "yarn run jest", "typecheck": "yarn tsc --skipLibCheck --noEmit", + "peptfilter": "yarn run tsx bin/peptfilter.ts", "uniprot": "yarn run tsx bin/uniprot.ts" }, "dependencies": {