|
| 1 | +module ArduinoCI |
| 2 | + |
| 3 | + # Information about an Arduino library package, as specified by the library.properties file |
| 4 | + # |
| 5 | + # See https://arduino.github.io/arduino-cli/library-specification/#libraryproperties-file-format |
| 6 | + class LibraryProperties |
| 7 | + |
| 8 | + # @return [Hash] The properties file parsed as a hash |
| 9 | + attr_reader :fields |
| 10 | + |
| 11 | + # @param path [Pathname] The path to the library.properties file |
| 12 | + def initialize(path) |
| 13 | + @fields = {} |
| 14 | + File.foreach(path) do |line| |
| 15 | + parts = line.split("=", 2) |
| 16 | + @fields[parts[0]] = parts[1].chomp unless parts.empty? |
| 17 | + end |
| 18 | + end |
| 19 | + |
| 20 | + # Enable a shortcut syntax for library property accessors, in the style of `attr_accessor` metaprogramming. |
| 21 | + # This is used to create a named field pointing to a specific property in the file, optionally applying |
| 22 | + # a specific formatting function. |
| 23 | + # |
| 24 | + # The formatting function MUST be a static method on this class. This is a limitation caused by the desire |
| 25 | + # to both (1) expose the formatters outside this class, and (2) use them for metaprogramming without the |
| 26 | + # having to name the entire function. field_reader is a static method, so if not for the fact that |
| 27 | + # `self.class.methods.include? formatter` fails to work for class methods in this context (unlike |
| 28 | + # `self.methods.include?`, which properly finds instance methods), I would allow either one and just |
| 29 | + # conditionally `define_method` the proper definition |
| 30 | + # |
| 31 | + # @param name [String] What the accessor will be called |
| 32 | + # @param field_num [Integer] The name of the key of the property |
| 33 | + # @param formatter [Symbol] The symbol for the formatting function to apply to the field (optional) |
| 34 | + # @return [void] |
| 35 | + # @macro [attach] field_reader |
| 36 | + # @!attribute [r] $1 |
| 37 | + # @return property $2 of the library.properties file, formatted with the function {$3} |
| 38 | + def self.field_reader(name, formatter = nil) |
| 39 | + key = name.to_s |
| 40 | + if formatter.nil? |
| 41 | + define_method(name) { @fields[key] } |
| 42 | + else |
| 43 | + define_method(name) { @fields.key?(key) ? self.class.send(formatter.to_sym, @fields[key]) : nil } |
| 44 | + end |
| 45 | + end |
| 46 | + |
| 47 | + # Parse a value as a comma-separated array |
| 48 | + # @param input [String] |
| 49 | + # @return [Array<String>] The individual values |
| 50 | + def self._csv(input) |
| 51 | + input.split(",").map(&:strip) |
| 52 | + end |
| 53 | + |
| 54 | + # Parse a value as a boolean |
| 55 | + # @param input [String] |
| 56 | + # @return [Array<String>] The individual values |
| 57 | + def self._bool(input) |
| 58 | + input == "true" # no indication given in the docs that anything but lowercase "true" indicates boolean true. |
| 59 | + end |
| 60 | + |
| 61 | + field_reader :name |
| 62 | + field_reader :version |
| 63 | + field_reader :author, :_csv |
| 64 | + field_reader :maintainer |
| 65 | + field_reader :sentence |
| 66 | + field_reader :paragraph |
| 67 | + field_reader :category |
| 68 | + field_reader :url |
| 69 | + field_reader :architectures, :_csv |
| 70 | + field_reader :depends, :_csv |
| 71 | + field_reader :dot_a_linkage, :_bool |
| 72 | + field_reader :includes, :_csv |
| 73 | + field_reader :precompiled, :_bool |
| 74 | + field_reader :ldflags, :_csv |
| 75 | + |
| 76 | + # The value of sentence always will be prepended, so you should start by writing the second sentence here |
| 77 | + # |
| 78 | + # (according to the docs) |
| 79 | + # @return [String] the sentence and paragraph together |
| 80 | + def full_paragraph |
| 81 | + [sentence, paragraph].join(" ") |
| 82 | + end |
| 83 | + |
| 84 | + end |
| 85 | + |
| 86 | +end |
0 commit comments