11# typed: strict
22# frozen_string_literal: true
33
4+ require "utils/spdx"
5+
46class FormulaStruct < T ::Struct
5- const :uses_from_macos , T ::Array [ T . any ( String , T ::Hash [ String , String ] ) ] , default : [ ]
6- const :requirements , T ::Array [ T ::Hash [ String , T . untyped ] ] , default : [ ]
7- const :dependencies , T ::Array [ String ] , default : [ ]
8- const :build_dependencies , T ::Array [ String ] , default : [ ]
9- const :test_dependencies , T ::Array [ String ] , default : [ ]
10- const :recommended_dependencies , T ::Array [ String ] , default : [ ]
11- const :optional_dependencies , T ::Array [ String ] , default : [ ]
12- const :stable_dependencies , T ::Hash [ String , T ::Array [ String ] ] , default : { dependencies : [ ] , build_dependencies : [ ] , test_dependencies : [ ] , recommended_dependencies : [ ] , optional_dependencies : [ ] , uses_from_macos_bounds : [ ] }
13- const :head_dependencies , T ::Hash [ String , T ::Array [ String ] ] , default : { dependencies : [ ] , build_dependencies : [ ] , test_dependencies : [ ] , recommended_dependencies : [ ] , optional_dependencies : [ ] , uses_from_macos_bounds : [ ] }
7+ # `:codesign` and custom requirement classes are not supported.
8+ API_SUPPORTED_REQUIREMENTS = [ :arch , :linux , :macos , :maximum_macos , :xcode ] . freeze
9+ private_constant :API_SUPPORTED_REQUIREMENTS
10+
11+ DeprecateDisableArgs = T . type_alias do
12+ {
13+ date : String ,
14+ because : T . nilable ( T . any ( String , Symbol ) ) ,
15+ replacement_formula : T . nilable ( String ) ,
16+ replacement_cask : T . nilable ( String ) ,
17+ }
18+ end
19+
20+ DependencyArgs = T . type_alias do
21+ T . any (
22+ # Formula name: "foo"
23+ String ,
24+ # Formula name and dependency type: { "foo" => :build }
25+ T ::Hash [ String , Symbol ] ,
26+ )
27+ end
28+
29+ RequirementArgs = T . type_alias do
30+ T . any (
31+ # Requirement name: :macos
32+ Symbol ,
33+ # Requirement name and other info: { macos: :build }
34+ T ::Hash [ Symbol , T ::Array [ T . anything ] ] ,
35+ )
36+ end
37+
38+ UsesFromMacOSArgs = T . type_alias do
39+ [
40+ T . any ( String , T ::Hash [ T . any ( String , Symbol ) , T . any ( Symbol , T ::Array [ Symbol ] ) ] ) ,
41+ T ::Hash [ Symbol , Symbol ] ,
42+ ]
43+ end
44+
45+ sig { params ( hash : T ::Hash [ String , T . untyped ] ) . returns ( FormulaStruct ) }
46+ def self . from_hash ( hash )
47+ hash [ "license" ] = SPDX . string_to_license_expression ( hash [ "license" ] )
48+
49+ if ( reason = hash [ "no_autobump_message" ] )
50+ reason = reason . to_sym if NO_AUTOBUMP_REASONS_LIST . key? ( reason . to_sym )
51+ hash [ "no_autobump_args" ] = { because : reason }
52+ end
53+
54+ if ( caveats = hash [ "caveats" ] )
55+ hash [ "caveats" ] = Formulary . replace_placeholders ( caveats )
56+ end
57+
58+ if ( condition = hash [ "pour_bottle_only_if" ] )
59+ hash [ "pour_bottle_args" ] = { only_if : condition . to_sym }
60+ end
61+
62+ if ( keg_only_hash = hash [ "keg_only_reason" ] )
63+ reason = Formulary . convert_to_string_or_symbol ( keg_only_hash . fetch ( "reason" ) )
64+ explanation = keg_only_hash . fetch ( "explanation" )
65+ hash [ "keg_only_args" ] = [ reason , explanation ]
66+ end
67+
68+ if ( deprecation_date = hash [ "deprecation_date" ] )
69+ hash [ "deprecate_args" ] = {
70+ date : deprecation_date ,
71+ because : DeprecateDisable . to_reason_string_or_symbol ( hash [ "deprecation_reason" ] , type : :formula ) ,
72+ replacement_formula : hash [ "deprecation_replacement_formula" ] ,
73+ replacement_cask : hash [ "deprecation_replacement_cask" ] ,
74+ }
75+ end
76+
77+ if ( disable_date = hash [ "disable_date" ] )
78+ hash [ "disable_args" ] = {
79+ date : disable_date ,
80+ because : DeprecateDisable . to_reason_string_or_symbol ( hash [ "disable_reason" ] , type : :formula ) ,
81+ replacement_formula : hash [ "disable_replacement_formula" ] ,
82+ replacement_cask : hash [ "disable_replacement_cask" ] ,
83+ }
84+ end
85+
86+ hash [ "stable_dependency_hash" ] = {
87+ "dependencies" => hash [ "dependencies" ] || [ ] ,
88+ "build_dependencies" => hash [ "build_dependencies" ] || [ ] ,
89+ "test_dependencies" => hash [ "test_dependencies" ] || [ ] ,
90+ "recommended_dependencies" => hash [ "recommended_dependencies" ] || [ ] ,
91+ "optional_dependencies" => hash [ "optional_dependencies" ] || [ ] ,
92+ "uses_from_macos" => hash [ "uses_from_macos" ] || [ ] ,
93+ "uses_from_macos_bounds" => hash [ "uses_from_macos_bounds" ] || [ ] ,
94+ }
95+
96+ conflicts_with = hash [ "conflicts_with" ] || [ ]
97+ conflicts_with_reasons = hash [ "conflicts_with_reasons" ] || [ ]
98+ hash [ "conflicts" ] = conflicts_with . zip ( conflicts_with_reasons ) . map do |name , because |
99+ [ name , { because : } ]
100+ end
101+
102+ # Rename some entries for clarity
103+ hash [ "link_overwrite_paths" ] = hash [ "link_overwrite" ]
104+ hash [ "requirements_array" ] = hash [ "requirements" ]
105+ hash [ "head_dependency_hash" ] = hash [ "head_dependencies" ]
106+
107+ super
108+ end
109+
110+ const :stable_dependency_hash , T ::Hash [ String , T ::Array [ String ] ] , default : { }
111+ const :head_dependency_hash , T ::Hash [ String , T ::Array [ String ] ] , default : { }
14112 const :caveats , T . nilable ( String )
15113 const :desc , String
16114 const :homepage , String
17- const :license , String
115+ const :license , SPDX :: LicenseExpression
18116 const :revision , Integer , default : 0
19117 const :version_scheme , Integer , default : 0
20- const :no_autobump_msg , T . nilable ( String )
118+ const :link_overwrite_paths , T ::Array [ String ] , default : [ ]
119+ const :post_install_defined , T ::Boolean , default : false
120+ const :service , T . nilable ( T ::Hash [ String , T . untyped ] )
121+ const :tap_git_head , String
122+ const :oldnames , T . nilable ( T ::Array [ String ] )
123+ const :ruby_source_path , String
124+ const :ruby_source_checksum , T ::Hash [ String , String ]
125+ const :aliases , T ::Array [ String ] , default : [ ]
126+ const :versioned_formulae , T ::Array [ String ] , default : [ ]
127+ const :bottle , T ::Hash [ String , T . untyped ] , default : { }
128+ const :no_autobump_args , T . nilable ( { because : T . any ( String , Symbol ) } )
129+ const :pour_bottle_args , T . nilable ( { only_if : Symbol } )
130+ const :keg_only_args , T . nilable ( [ T . any ( String , Symbol ) , String ] )
131+ const :deprecate_args , T . nilable ( DeprecateDisableArgs )
132+ const :disable_args , T . nilable ( DeprecateDisableArgs )
133+ const :conflicts , T ::Array [ [ String , { because : T . nilable ( String ) } ] ] , default : [ ]
134+
135+ sig { returns ( String ) }
136+ def ruby_source_sha256
137+ ruby_source_checksum . fetch ( "sha256" )
138+ end
139+
140+ sig { returns ( T ::Boolean ) }
141+ def head?
142+ urls [ "head" ] . present?
143+ end
144+
145+ sig { returns ( [ String , T ::Hash [ Symbol , T . anything ] ] ) }
146+ def head_url_args
147+ url = T . must ( urls . dig ( "head" , "url" ) )
148+ specs = {
149+ branch : urls . dig ( "head" , "branch" ) ,
150+ using : urls . dig ( "head" , "using" ) &.to_sym ,
151+ } . compact_blank
152+ [ url , specs ]
153+ end
154+
155+ sig { returns ( T ::Array [ T . any ( DependencyArgs , RequirementArgs ) ] ) }
156+ def head_dependencies
157+ spec_dependencies ( :head ) + spec_requirements ( :head )
158+ end
159+
160+ sig { returns ( T ::Array [ UsesFromMacOSArgs ] ) }
161+ def head_uses_from_macos
162+ spec_uses_from_macos ( :head )
163+ end
164+
165+ sig { returns ( T ::Boolean ) }
166+ def stable?
167+ urls [ "stable" ] . present?
168+ end
169+
170+ sig { returns ( [ String , T ::Hash [ Symbol , T . anything ] ] ) }
171+ def stable_url_args
172+ url = T . must ( urls . dig ( "stable" , "url" ) )
173+ specs = {
174+ tag : urls . dig ( "stable" , "tag" ) ,
175+ revision : urls . dig ( "stable" , "revision" ) ,
176+ using : urls . dig ( "stable" , "using" ) &.to_sym ,
177+ } . compact_blank
178+ [ url , specs ]
179+ end
180+
181+ sig { returns ( String ) }
182+ def stable_version
183+ versions . fetch ( "stable" )
184+ end
185+
186+ sig { returns ( T . nilable ( String ) ) }
187+ def stable_checksum
188+ urls . dig ( "stable" , "checksum" )
189+ end
190+
191+ sig { returns ( T ::Array [ T . any ( DependencyArgs , RequirementArgs ) ] ) }
192+ def stable_dependencies
193+ spec_dependencies ( :stable ) + spec_requirements ( :stable )
194+ end
195+
196+ sig { returns ( T ::Array [ UsesFromMacOSArgs ] ) }
197+ def stable_uses_from_macos
198+ spec_uses_from_macos ( :stable )
199+ end
200+
201+ sig { returns ( T ::Boolean ) }
202+ def bottle?
203+ bottle [ "stable" ] . present?
204+ end
205+
206+ sig { returns ( Integer ) }
207+ def bottle_rebuild
208+ bottle . dig ( "stable" , "rebuild" ) || 0
209+ end
210+
211+ sig { returns ( T ::Array [ T ::Hash [ String , T . anything ] ] ) }
212+ def bottle_checksums
213+ Array ( bottle . dig ( "stable" , "files" ) ) . map do |tag , bottle_spec |
214+ {
215+ cellar : Formulary . convert_to_string_or_symbol ( bottle_spec . fetch ( "cellar" ) ) ,
216+ tag . to_sym => bottle_spec . fetch ( "sha256" ) ,
217+ }
218+ end
219+ end
220+
221+ # sig { returns(T::Boolean) }
222+ # def service?
223+ # service.present?
224+ # end
225+
226+ # def service_run_args
227+ # end
228+
229+ # def service_name_args
230+ # end
231+
232+ # def service_methods_and_args
233+ # end
234+
235+ private
236+
237+ const :versions , T ::Hash [ String , String ]
238+ const :urls , T ::Hash [ String , T ::Hash [ String , T . nilable ( String ) ] ]
239+ const :requirements_array , T ::Array [ T ::Hash [ String , T . untyped ] ] , default : [ ]
240+ const :no_autobump_message , T . nilable ( String )
21241 const :pour_bottle_only_if , T . nilable ( String )
22242 const :keg_only_reason , T . nilable ( T ::Hash [ String , String ] )
23243 const :deprecation_date , T . nilable ( String )
@@ -30,33 +250,75 @@ class FormulaStruct < T::Struct
30250 const :disable_replacement_cask , T . nilable ( String )
31251 const :conflicts_with , T ::Array [ String ] , default : [ ]
32252 const :conflicts_with_reasons , T ::Array [ String ] , default : [ ]
33- const :link_overwrite , T ::Array [ String ] , default : [ ]
34- const :post_install_defined , T ::Boolean , default : false
35- const :service , T . nilable ( T ::Hash [ String , T . untyped ] )
36- const :tap_git_head , String
37- const :oldnames , T . nilable ( T ::Array [ String ] )
38- const :oldname , T . nilable ( String )
39- const :ruby_source_path , String
40- const :ruby_source_checksum , T ::Hash [ String , String ]
41- const :urls , T ::Hash [ String , T ::Hash [ String , T . nilable ( String ) ] ]
42- const :ruby_source_sha256 , T . nilable ( String )
43- const :aliases , T ::Array [ String ] , default : [ ]
44- const :versioned_formulae , T ::Array [ String ] , default : [ ]
45- const :versions , T ::Hash [ String , String ]
46- const :bottle , T ::Hash [ String , T . untyped ] , default : { }
47- const :uses_from_macos_bounds , T . nilable ( T ::Array [ T ::Hash [ String , String ] ] )
48253
49- sig { params ( hash : T ::Hash [ String , T . untyped ] ) . returns ( FormulaStruct ) }
50- def self . from_hash ( hash )
51- hash [ "stable_dependencies" ] ||= {
52- " dependencies" => hash [ "dependencies" ] || [ ] ,
53- "build_dependencies" => hash [ "build_dependencies" ] || [ ] ,
54- "test_dependencies" => hash [ "test_dependencies" ] || [ ] ,
55- "recommended_dependencies" => hash [ "recommended_dependencies" ] || [ ] ,
56- "optional_dependencies" => hash [ "optional_dependencies" ] || [ ] ,
57- "uses_from_macos_bounds" => hash [ "uses_from_macos_bounds" ] || [ ] ,
58- }
254+ sig { params ( spec : Symbol ) . returns ( T ::Array [ DependencyArgs ] ) }
255+ def spec_dependencies ( spec )
256+ deps_hash = send ( " #{ spec } _dependency_hash" )
257+ dependencies = deps_hash . fetch ( "dependencies" , [ ] )
258+ dependencies + [ :build , :test , :recommended , :optional ] . filter_map do | type |
259+ deps_hash [ " #{ type } _dependencies" ] &. map do | dep |
260+ { dep => type }
261+ end
262+ end . flatten ( 1 )
263+ end
59264
60- super
265+ sig { params ( spec : Symbol ) . returns ( T ::Array [ UsesFromMacOSArgs ] ) }
266+ def spec_uses_from_macos ( spec )
267+ deps_hash = send ( "#{ spec } _dependency_hash" )
268+ zipped_array = deps_hash [ "uses_from_macos" ] &.zip ( deps_hash [ "uses_from_macos_bounds" ] )
269+ return [ ] unless zipped_array
270+
271+ zipped_array . map do |entry , bounds |
272+ bounds ||= { }
273+ bounds = bounds . transform_keys ( &:to_sym ) . transform_values ( &:to_sym )
274+
275+ if entry . is_a? ( Hash )
276+ # The key is the dependency name, the value is the dep type. Only the type should be a symbol
277+ entry = entry . deep_transform_values ( &:to_sym )
278+ # When passing both a dep type and bounds, uses_from_macos expects them both in the first argument
279+ entry = entry . merge ( bounds )
280+ [ entry , { } ]
281+ else
282+ [ entry , bounds ]
283+ end
284+ end
285+ end
286+
287+ sig { params ( spec : Symbol ) . returns ( T ::Array [ RequirementArgs ] ) }
288+ def spec_requirements ( spec )
289+ requirements_array . filter_map do |req |
290+ next unless req [ "specs" ] . include? ( spec . to_s )
291+
292+ req_name = req [ "name" ] . to_sym
293+ next if API_SUPPORTED_REQUIREMENTS . exclude? ( req_name )
294+
295+ req_version = case req_name
296+ when :arch
297+ req [ "version" ] &.to_sym
298+ when :macos , :maximum_macos
299+ MacOSVersion ::SYMBOLS . key ( req [ "version" ] )
300+ else
301+ req [ "version" ]
302+ end
303+
304+ req_tags = [ ]
305+ req_tags << req_version if req_version . present?
306+ req_tags += req [ "contexts" ] &.map do |tag |
307+ case tag
308+ when String
309+ tag . to_sym
310+ when Hash
311+ tag . deep_transform_keys ( &:to_sym )
312+ else
313+ tag
314+ end
315+ end
316+
317+ if req_tags . empty?
318+ req_name
319+ else
320+ { req_name => req_tags }
321+ end
322+ end
61323 end
62324end
0 commit comments