Files
discourse-hledger/lib/hledger/journal.rb
T
2026-09-27 10:31:47 +02:00

55 lines
1.4 KiB
Ruby

# frozen_string_literal: true
module Hledger
# Extracts the single fenced `hledger` journal from a post's raw markdown and
# rejects input that could make hledger read files or resources we do not
# control.
class Journal
FENCE = /^[ \t]*`{3,}hledger[ \t]*\r?\n(?<source>.*?)^[ \t]*`{3,}[ \t]*$/m
INCLUDE = /^[ \t]*!?include\b/i
MAX_LINES = 5_000
Result =
Struct.new(:source, :error, keyword_init: true) do
def ok?
error.nil?
end
def none?
error == :none
end
end
def self.extract(raw)
new(raw).extract
end
def initialize(raw)
@raw = raw.to_s
end
def extract
matches = @raw.scan(FENCE).flatten
return Result.new(source: nil, error: :none) if matches.empty?
return Result.new(source: nil, error: :ambiguous) if matches.length > 1
source = matches.first
validation_error = validate(source)
return Result.new(source: nil, error: validation_error) if validation_error
Result.new(source: source, error: nil)
end
private
def validate(source)
return :too_large if source.bytesize > SiteSetting.hledger_max_journal_bytes
return :too_many_lines if source.lines.size > MAX_LINES
return :invalid_encoding unless source.valid_encoding?
return :include_not_allowed if source.match?(INCLUDE)
nil
end
end
end