mirror of
https://github.com/hkalexling/Mango.git
synced 2025-08-02 10:55:30 -04:00
152 lines
3.3 KiB
Crystal
152 lines
3.3 KiB
Crystal
module Rename
|
|
alias VHash = Hash(String, String)
|
|
|
|
abstract class Base(T)
|
|
@ary = [] of T
|
|
|
|
def push(var)
|
|
@ary.push var
|
|
end
|
|
|
|
abstract def render(hash : VHash)
|
|
end
|
|
|
|
class Variable < Base(String)
|
|
property id : String
|
|
|
|
def initialize(@id)
|
|
end
|
|
|
|
def render(hash : VHash)
|
|
hash[@id]? || ""
|
|
end
|
|
end
|
|
|
|
class Pattern < Base(Variable)
|
|
def render(hash : VHash)
|
|
@ary.each do |v|
|
|
if hash.has_key? v.id
|
|
return v.render hash
|
|
end
|
|
end
|
|
""
|
|
end
|
|
end
|
|
|
|
class Group < Base(Pattern | String)
|
|
def render(hash : VHash)
|
|
return "" if @ary.select(Pattern)
|
|
.any? &.as(Pattern).render(hash).empty?
|
|
@ary.join do |e|
|
|
if e.is_a? Pattern
|
|
e.render hash
|
|
else
|
|
e
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
class Rule < Base(Group | String | Pattern)
|
|
ESCAPE = ['/']
|
|
|
|
def initialize(str : String)
|
|
parse! str
|
|
rescue e
|
|
raise "Failed to parse rename rule #{str}. Error: #{e}"
|
|
end
|
|
|
|
private def parse!(str : String)
|
|
chars = [] of Char
|
|
pattern : Pattern? = nil
|
|
group : Group? = nil
|
|
|
|
str.each_char_with_index do |char, i|
|
|
if ['[', ']', '{', '}', '|'].includes?(char) && !chars.empty?
|
|
string = chars.join
|
|
if !pattern.nil?
|
|
pattern.push Variable.new string.strip
|
|
elsif !group.nil?
|
|
group.push string
|
|
else
|
|
@ary.push string
|
|
end
|
|
chars = [] of Char
|
|
end
|
|
|
|
case char
|
|
when '['
|
|
if !group.nil? || !pattern.nil?
|
|
raise "nested groups are not allowed"
|
|
end
|
|
group = Group.new
|
|
when ']'
|
|
if group.nil?
|
|
raise "unmatched ] at position #{i}"
|
|
end
|
|
if !pattern.nil?
|
|
raise "patterns (`{}`) should be closed before closing the " \
|
|
"group (`[]`)"
|
|
end
|
|
@ary.push group
|
|
group = nil
|
|
when '{'
|
|
if !pattern.nil?
|
|
raise "nested patterns are not allowed"
|
|
end
|
|
pattern = Pattern.new
|
|
when '}'
|
|
if pattern.nil?
|
|
raise "unmatched } at position #{i}"
|
|
end
|
|
if !group.nil?
|
|
group.push pattern
|
|
else
|
|
@ary.push pattern
|
|
end
|
|
pattern = nil
|
|
when '|'
|
|
if pattern.nil?
|
|
chars.push char
|
|
end
|
|
else
|
|
if ESCAPE.includes? char
|
|
raise "the character #{char} at position #{i} is not allowed"
|
|
end
|
|
chars.push char
|
|
end
|
|
end
|
|
|
|
unless chars.empty?
|
|
@ary.push chars.join
|
|
end
|
|
if !pattern.nil?
|
|
raise "unclosed pattern {"
|
|
end
|
|
if !group.nil?
|
|
raise "unclosed group ["
|
|
end
|
|
end
|
|
|
|
def render(hash : VHash)
|
|
str = @ary.join do |e|
|
|
if e.is_a? String
|
|
e
|
|
else
|
|
e.render hash
|
|
end
|
|
end.strip
|
|
post_process str
|
|
end
|
|
|
|
# Post-processes the generated file/folder name
|
|
# - Handles the rare case where the string is `..`
|
|
# - Removes trailing spaces and periods
|
|
# - Replace illegal characters with `_`
|
|
private def post_process(str)
|
|
return "_" if str == ".."
|
|
str.rstrip(" .").gsub /[\/?<>\\:*|"^]/, "_"
|
|
end
|
|
end
|
|
end
|