import { segment } from './segment'
const NUMERICAL_RANGE = /^(-?\d+)\.\.(-?\d+)(?:\.\.(-?\d+))?$/
export function expand(pattern: string): string[] {
let index = pattern.indexOf('{')
if (index === -1) return [pattern]
let result: string[] = []
let pre = pattern.slice(0, index)
let rest = pattern.slice(index)
let depth = 0
let endIndex = rest.lastIndexOf('}')
for (let i = 0; i < rest.length; i++) {
let char = rest[i]
if (char === '{') {
depth++
} else if (char === '}') {
depth--
if (depth === 0) {
endIndex = i
break
}
}
}
if (endIndex === -1) {
throw new Error(`The pattern \`${pattern}\` is not balanced.`)
}
let inside = rest.slice(1, endIndex)
let post = rest.slice(endIndex + 1)
let parts: string[]
if (isSequence(inside)) {
parts = expandSequence(inside)
} else {
parts = segment(inside, ',')
}
parts = parts.flatMap((part) => expand(part))
let expandedTail = expand(post)
for (let tail of expandedTail) {
for (let part of parts) {
result.push(pre + part + tail)
}
}
return result
}
function isSequence(str: string): boolean {
return NUMERICAL_RANGE.test(str)
}
function expandSequence(seq: string): string[] {
let seqMatch = seq.match(NUMERICAL_RANGE)
if (!seqMatch) {
return [seq]
}
let [, start, end, stepStr] = seqMatch
let step = stepStr ? parseInt(stepStr, 10) : undefined
let result: string[] = []
if (/^-?\d+$/.test(start) && /^-?\d+$/.test(end)) {
let startNum = parseInt(start, 10)
let endNum = parseInt(end, 10)
let padLength = Math.max(start.replace(/^-/, '').length, end.replace(/^-/, '').length)
if (step === undefined) {
step = startNum <= endNum ? 1 : -1
}
if (step === 0) {
throw new Error('Step cannot be zero in sequence expansion.')
}
if (step > 0) {
for (let i = startNum; i <= endNum; i += step) {
let numStr = i.toString()
if (numStr.length < padLength) {
numStr = numStr.padStart(padLength, '0')
}
result.push(numStr)
}
} else {
for (let i = startNum; i >= endNum; i += step) {
let numStr = i.toString()
if (numStr.length < padLength) {
numStr = numStr.padStart(padLength, '0')
}
result.push(numStr)
}
}
}
return result
}