lib/mootool/models/device_tree.rb in mootool-0.2.3 vs lib/mootool/models/device_tree.rb in mootool-0.2.4

- old
+ new

@@ -2,84 +2,116 @@ # typed: true require 'sorbet-runtime' module MooTool - module Models - # DeviceTree is a Apple Device Tree node that has been parsed - class DeviceTree + # DeviceTree is a Apple Device Tree node that has been parsed + class DeviceTree + extend T::Sig + + NODE_FORMAT = 'VV' + PROP_FORMAT = 'A32V' + + PHANDLE_PROP = 'AAPL,phandle' + COMPATIBLE_PROP = 'compatible' + + # Represents a single node in the device tree + class Node extend T::Sig - NODE_FORMAT = 'VV' - PROP_FORMAT = 'A32V' + attr_reader :children, :properties - # Represents a single node in the device tree - class Node - extend T::Sig + sig { params(tree: DeviceTree, data: T.any(IO, StringIO)).void } + def initialize(tree, data) + @tree = tree + vals = T.must(data.read(8)).unpack(NODE_FORMAT) + property_count = T.cast(vals[0], Integer) + child_count = T.cast(vals[1], Integer) - attr_reader :children, :properties + @properties = T.let({}, T::Hash[String, Property]) + @children = T.let([], T::Array[Node]) - sig { params(data: T.any(IO, StringIO)).void } - def initialize(data) - vals = T.must(data.read(8)).unpack(NODE_FORMAT) - property_count = T.cast(vals[0], Integer) - child_count = T.cast(vals[1], Integer) + property_count.times do + prop = Property.new(data) + @properties[prop.name] = prop + end + child_count.times { @children << Node.new(tree, data) } - @properties = T.let({}, T::Hash[String, Property]) - @children = T.let([], T::Array[Node]) + @tree.add_handle(self, @properties[PHANDLE_PROP].value) if @properties.key? PHANDLE_PROP + end - property_count.times do - prop = Property.new(data) - @properties[prop.name] = prop - end - child_count.times { @children << Node.new(data) } + def to_h + props = @properties.transform_values(&:value) + if @children.any? + props.merge({ children: @children.map(&:to_h) }) + else + props end end + end - # Represents a single property and it's value - class Property - extend T::Sig + # Represents a single property and it's value + class Property + extend T::Sig - attr_accessor :name, :value + attr_accessor :name, :value - sig { params(data: T.any(StringIO, IO)).void } - def initialize(data) - args = T.must(data.read(36)).unpack(PROP_FORMAT) + sig { params(data: T.any(StringIO, IO)).void } + def initialize(data) + args = T.must(data.read(36)).unpack(PROP_FORMAT) - @name = T.let(T.cast(args[0], String), String) - @size = T.let(T.cast(args[1], Integer), Integer) + @name = T.let(T.cast(args[0], String), String) + @size = T.let(T.cast(args[1], Integer), Integer) - if @size & 0x80000000 != 0 - @template = true - @size &= 0x7fffffff - end - - @value = data.read(@size.align(4)) - @value = T.must(@value)[0..@size] + if @size & 0x80000000 != 0 + @template = true + @size &= 0x7fffffff end + + @value = T.must(data.read(@size.align(4)))[0..(@size - 1)] + + normalize end - attr_reader :root + private - # @param [String] data - sig { params(data: T.any(IO, String, StringIO, Pathname)).void } - def initialize(data) - case data - when Pathname - data = T.assert_type!(data, Pathname) - @data = File.open(data.realpath, 'rb') - when String - data = T.assert_type!(data, String) - @data = StringIO.new(data) - when IO - @data = T.cast(data, IO) - end - @root = Node.new(@data) + def normalize + @value = @value.split("\x00").map(&:chomp) if @name == COMPATIBLE_PROP + @value = @value.unpack1('V') if @size == 4 + + @value = @value.chomp("\x00") if @value.is_a?(String) && @value.count("\x00") == 1 && @value.end_with?("\x00") end + end - sig { params(path: String).returns(DeviceTree) } - def self.open(path) - DeviceTree.new(Pathname.new(path)) + attr_reader :root + + # @param [String] data + sig { params(data: T.any(IO, String, StringIO, Pathname)).void } + def initialize(data) + @handles = {} + case data + when Pathname + data = T.assert_type!(data, Pathname) + @data = File.open(data.realpath, 'rb') + when String + data = T.assert_type!(data, String) + @data = StringIO.new(data) + when IO + @data = T.cast(data, IO) end + @root = Node.new(self, @data) + end + + def add_handle(node, handle) + @handles[handle] = node + end + + sig { params(path: String).returns(DeviceTree) } + def self.open(path) + MooTool::DeviceTree.new(Pathname.new(path)) + end + + def to_h + @root.to_h end end end