Files
picopackage/lib/picopackage/source_file.rb
2025-01-21 08:29:23 +11:00

109 lines
2.8 KiB
Ruby

require "yaml"
require "digest"
module Picop
class SourceFile
attr_reader :content, :metadata, :code, :original_path
METADATA_PATTERN = /^\n*#\s*@PICOPACKAGE_START\n(.*?)^\s*#\s*@PICOPACKAGE_END\s*$/m
def self.from_file(file_path) = new(content: File.read(file_path), original_path: file_path)
def self.from_content(content, filename: nil)
instance = new(content: content)
if filename && !instance.metadata['filename']
metadata = instance.metadata.merge('filename' => filename)
instance.update_metadata(metadata) #TODO: FIX THIS
end
instance
end
def initialize(content:, original_path: nil)
@original_path = original_path
@content = content
@metadata = extract_metadata
@code = extract_code
end
def filename = @metadata['filename']
def version = @metadata['version'] || '0.0.0'
def checksum = "sha256:#{Digest::SHA256.hexdigest(code)}"
def inspect_metadata = puts JSON.pretty_generate(@metadata)
def save(destination = nil)
path = determine_save_path(destination)
File.write(path, content)
path
end
def extract_code = content.sub(METADATA_PATTERN, '')
def extract_metadata
return {} unless content =~ METADATA_PATTERN
yaml_content = $1.lines.map do |line|
line.sub(/^\s*#\s?/, '').rstrip
end.join("\n")
YAML.safe_load(yaml_content)
end
def update_metadata(metadata_hash)
@metadata = metadata_hash
@content = generate_content
end
def sign
hash = checksum
return puts "File already signed" if metadata['content_checksum'] == hash
new_metadata = metadata.merge('content_checksum' => hash)
update_metadata(new_metadata)
save
end
def verify
return false unless metadata.key? 'content_checksum'
checksum == metadata['content_checksum']
end
def modified? = !verify
private
def generate_content
metadata_block = generate_metadata
if content =~ METADATA_PATTERN
content.sub(METADATA_PATTERN, "\n#{metadata_block}")
else
[content.rstrip, "\n#{metadata_block}"].join("\n")
end
end
# This will need a comment style one day, to work with other languages
def generate_metadata
yaml_content = @metadata.to_yaml.strip
[
"# @PICOPACKAGE_START",
yaml_content.lines.map { |line| "# #{line}" }.join,
"# @PICOPACKAGE_END",
""
].join("\n")
end
def determine_save_path(destination)
if destination.nil?
@original_path || filename || raise("No filename available")
elsif File.directory?(destination)
File.join(destination, filename || File.basename(@original_path))
else
destination
end
end
end
end