97 lines
2.9 KiB
TypeScript
97 lines
2.9 KiB
TypeScript
// mime-todo/lib/issue.ts
|
|
|
|
|
|
export type RelationshipKind = "dependsOn" | "relatesTo" | "blocks"
|
|
|
|
export interface IssueRelationships {
|
|
[kind: string]: number[]
|
|
}
|
|
|
|
export type IssueType = "feature" | "bugfix" | "hotfix"
|
|
export type IssueStatus = "open" | "in-progress" | "done" | "hold" | "cancelled"
|
|
export type IssuePriority = "low" | "medium" | "high"
|
|
|
|
export interface Issue {
|
|
id: number
|
|
type: IssueType
|
|
title: string
|
|
status: IssueStatus
|
|
priority: IssuePriority
|
|
created: string // YYYY-MM-DD
|
|
relationships: IssueRelationships
|
|
dueStart?: string // YYYY-MM-DD
|
|
dueEnd?: string // YYYY-MM-DD
|
|
description: string
|
|
body: string
|
|
}
|
|
|
|
export function parseRelationships(text: string): IssueRelationships {
|
|
const relationships: IssueRelationships = {}
|
|
|
|
// Handle empty relationships
|
|
if (!text || text.trim() === "") {
|
|
return relationships
|
|
}
|
|
|
|
// Simple parsing for format like "dependsOn:3"
|
|
const parts = text.split(/,\s*/).filter(p => p.trim() !== "")
|
|
for (const part of parts) {
|
|
const [key, value] = part.split(":").map(s => s.trim())
|
|
if (key && value) {
|
|
relationships[key] = value.split(/[\s]+/).map(Number).filter(n => !isNaN(n))
|
|
}
|
|
}
|
|
|
|
return relationships
|
|
}
|
|
|
|
export function parseIssue(text: string): Issue {
|
|
const lines = text.split(/\r?\n/)
|
|
const issue: Partial<Issue> = {}
|
|
|
|
let inDescription = false
|
|
const descLines: string[] = []
|
|
const bodyLines: string[] = []
|
|
|
|
for (const line of lines) {
|
|
if (!inDescription) {
|
|
if (line.startsWith("ID:")) issue.id = Number(line.slice(3).trim())
|
|
else if (line.startsWith("Type:")) issue.type = line.slice(5).trim()
|
|
else if (line.startsWith("Title:")) issue.title = line.slice(6).trim()
|
|
else if (line.startsWith("Status:")) issue.status = line.slice(7).trim()
|
|
else if (line.startsWith("Priority:")) issue.priority = line.slice(9).trim()
|
|
else if (line.startsWith("Created:")) issue.created = line.slice(8).trim()
|
|
else if (line.startsWith("DueStart:")) issue.dueStart = line.slice(9).trim()
|
|
else if (line.startsWith("DueEnd:")) issue.dueEnd = line.slice(7).trim()
|
|
else if (line.startsWith("Relationships:")) {
|
|
const rel = line.slice("Relationships:".length).trim()
|
|
issue.relationships = parseRelationships(rel)
|
|
}
|
|
else if (line.startsWith("Description:")) {
|
|
inDescription = true
|
|
descLines.push(line.slice("Description:".length).trim())
|
|
}
|
|
} else {
|
|
// Description continuation
|
|
if (line.trim() === "") {
|
|
// blank line ends description
|
|
inDescription = false
|
|
continue
|
|
}
|
|
|
|
if (/^\s+/.test(line)) {
|
|
descLines.push(line.trim())
|
|
continue
|
|
}
|
|
|
|
// first non-indented line ends description
|
|
inDescription = false
|
|
bodyLines.push(line)
|
|
}
|
|
}
|
|
|
|
issue.description = descLines.join("\n")
|
|
issue.body = bodyLines.join("\n")
|
|
|
|
return issue as Issue
|
|
}
|