sorbet-baml 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +94 -0
  3. data/README.md +315 -122
  4. data/Rakefile +2 -2
  5. data/docs-site/.gitignore +48 -0
  6. data/docs-site/Gemfile +5 -0
  7. data/docs-site/Gemfile.lock +140 -0
  8. data/docs-site/Rakefile +3 -0
  9. data/docs-site/bridgetown.config.yml +15 -0
  10. data/docs-site/config/initializers.rb +9 -0
  11. data/docs-site/config/puma.rb +9 -0
  12. data/docs-site/config.ru +5 -0
  13. data/docs-site/esbuild.config.js +11 -0
  14. data/docs-site/frontend/javascript/index.js +22 -0
  15. data/docs-site/frontend/styles/index.css +61 -0
  16. data/docs-site/package.json +18 -0
  17. data/docs-site/postcss.config.js +6 -0
  18. data/docs-site/server/roda_app.rb +9 -0
  19. data/docs-site/src/_components/head.liquid +26 -0
  20. data/docs-site/src/_components/nav.liquid +68 -0
  21. data/docs-site/src/_layouts/default.liquid +27 -0
  22. data/docs-site/src/_layouts/doc.liquid +39 -0
  23. data/docs-site/src/advanced-usage.md +598 -0
  24. data/docs-site/src/getting-started.md +170 -0
  25. data/docs-site/src/index.md +183 -0
  26. data/docs-site/src/troubleshooting.md +317 -0
  27. data/docs-site/src/type-mapping.md +236 -0
  28. data/docs-site/tailwind.config.js +85 -0
  29. data/examples/description_parameters.rb +49 -0
  30. data/lib/sorbet_baml/comment_extractor.rb +51 -54
  31. data/lib/sorbet_baml/converter.rb +69 -35
  32. data/lib/sorbet_baml/dependency_resolver.rb +11 -11
  33. data/lib/sorbet_baml/description_extension.rb +34 -0
  34. data/lib/sorbet_baml/description_extractor.rb +34 -0
  35. data/lib/sorbet_baml/dspy_tool_converter.rb +97 -0
  36. data/lib/sorbet_baml/dspy_tool_extensions.rb +23 -0
  37. data/lib/sorbet_baml/enum_extensions.rb +2 -2
  38. data/lib/sorbet_baml/struct_extensions.rb +2 -2
  39. data/lib/sorbet_baml/tool_extensions.rb +23 -0
  40. data/lib/sorbet_baml/type_mapper.rb +35 -37
  41. data/lib/sorbet_baml/version.rb +1 -1
  42. data/lib/sorbet_baml.rb +41 -10
  43. data/sorbet/config +2 -0
  44. data/sorbet/rbi/gems/anthropic@1.5.0.rbi +21252 -0
  45. data/sorbet/rbi/gems/async@2.27.3.rbi +9 -0
  46. data/sorbet/rbi/gems/bigdecimal@3.2.2.rbi +9 -0
  47. data/sorbet/rbi/gems/concurrent-ruby@1.3.5.rbi +424 -0
  48. data/sorbet/rbi/gems/connection_pool@2.5.3.rbi +9 -0
  49. data/sorbet/rbi/gems/console@1.33.0.rbi +9 -0
  50. data/sorbet/rbi/gems/dry-configurable@1.3.0.rbi +672 -0
  51. data/sorbet/rbi/gems/dry-core@1.1.0.rbi +1729 -0
  52. data/sorbet/rbi/gems/dry-logger@1.1.0.rbi +1317 -0
  53. data/sorbet/rbi/gems/dspy@0.19.1.rbi +6677 -0
  54. data/sorbet/rbi/gems/ffi@1.17.2.rbi +2174 -0
  55. data/sorbet/rbi/gems/fiber-annotation@0.2.0.rbi +9 -0
  56. data/sorbet/rbi/gems/fiber-local@1.1.0.rbi +9 -0
  57. data/sorbet/rbi/gems/fiber-storage@1.0.1.rbi +9 -0
  58. data/sorbet/rbi/gems/google-protobuf@4.32.0.rbi +9 -0
  59. data/sorbet/rbi/gems/googleapis-common-protos-types@1.20.0.rbi +9 -0
  60. data/sorbet/rbi/gems/informers@1.2.1.rbi +1875 -0
  61. data/sorbet/rbi/gems/io-event@1.12.1.rbi +9 -0
  62. data/sorbet/rbi/gems/metrics@0.13.0.rbi +9 -0
  63. data/sorbet/rbi/gems/onnxruntime@0.10.0.rbi +304 -0
  64. data/sorbet/rbi/gems/openai@0.16.0.rbi +68055 -0
  65. data/sorbet/rbi/gems/opentelemetry-api@1.6.0.rbi +9 -0
  66. data/sorbet/rbi/gems/opentelemetry-common@0.22.0.rbi +9 -0
  67. data/sorbet/rbi/gems/opentelemetry-exporter-otlp@0.30.0.rbi +9 -0
  68. data/sorbet/rbi/gems/opentelemetry-registry@0.4.0.rbi +9 -0
  69. data/sorbet/rbi/gems/opentelemetry-sdk@1.8.1.rbi +9 -0
  70. data/sorbet/rbi/gems/opentelemetry-semantic_conventions@1.11.0.rbi +9 -0
  71. data/sorbet/rbi/gems/polars-df@0.20.0.rbi +9 -0
  72. data/sorbet/rbi/gems/sorbet-result@1.4.0.rbi +242 -0
  73. data/sorbet/rbi/gems/sorbet-schema@0.9.2.rbi +743 -0
  74. data/sorbet/rbi/gems/sorbet-struct-comparable@1.3.0.rbi +48 -0
  75. data/sorbet/rbi/gems/tokenizers@0.5.5.rbi +754 -0
  76. data/sorbet/rbi/gems/traces@0.17.0.rbi +9 -0
  77. data/sorbet/rbi/gems/zeitwerk@2.7.3.rbi +1429 -0
  78. metadata +67 -7
  79. data/docs/README.md +0 -117
  80. data/docs/advanced-usage.md +0 -427
  81. data/docs/getting-started.md +0 -91
  82. data/docs/troubleshooting.md +0 -291
  83. data/docs/type-mapping.md +0 -192
@@ -0,0 +1,236 @@
1
+ ---
2
+ layout: doc
3
+ title: "Type Mapping Reference"
4
+ description: "Complete mapping between Sorbet types and BAML output. Learn how every Sorbet type converts to efficient BAML format."
5
+ ---
6
+
7
+ # Type Mapping Reference
8
+
9
+ Complete mapping between Sorbet types and BAML output for autonomous LLM workflows. All listed types are **fully supported** with automatic field descriptions.
10
+
11
+ ## Basic Types
12
+
13
+ | Sorbet Type | BAML Output | Example |
14
+ |-------------|-------------|---------|
15
+ | `String` | `string` | `name string` |
16
+ | `Integer` | `int` | `age int` |
17
+ | `Float` | `float` | `price float` |
18
+ | `T::Boolean` | `bool` | `active bool` |
19
+ | `NilClass` | `null` | `null` |
20
+ | `Symbol` | `string` | `status string` |
21
+ | `Date/DateTime/Time` | `string` | `created_at string` |
22
+
23
+ ## Optional Types (T.nilable)
24
+
25
+ | Sorbet Type | BAML Output | Example |
26
+ |-------------|-------------|---------|
27
+ | `T.nilable(String)` | `string?` | `email string?` |
28
+ | `T.nilable(Integer)` | `int?` | `age int?` |
29
+ | `T.nilable(MyStruct)` | `MyStruct?` | `address Address?` |
30
+
31
+ ## Collection Types
32
+
33
+ | Sorbet Type | BAML Output | Example |
34
+ |-------------|-------------|---------|
35
+ | `T::Array[String]` | `string[]` | `tags string[]` |
36
+ | `T::Array[Integer]` | `int[]` | `scores int[]` |
37
+ | `T::Array[MyStruct]` | `MyStruct[]` | `addresses Address[]` |
38
+
39
+ ## Hash/Map Types
40
+
41
+ | Sorbet Type | BAML Output | Example |
42
+ |-------------|-------------|---------|
43
+ | `T::Hash[String, String]` | `map<string, string>` | `metadata map<string, string>` |
44
+ | `T::Hash[String, Integer]` | `map<string, int>` | `counts map<string, int>` |
45
+ | `T::Hash[Symbol, String]` | `map<string, string>` | `config map<string, string>` |
46
+
47
+ ## Union Types (T.any)
48
+
49
+ | Sorbet Type | BAML Output | Example |
50
+ |-------------|-------------|---------|
51
+ | `T.any(String, Integer)` | `string \| int` | `value string \| int` |
52
+ | `T.any(String, Integer, Float)` | `string \| int \| float` | `mixed string \| int \| float` |
53
+ | `T.nilable(T.any(String, Integer))` | `(string \| int)?` | `optional (string \| int)?` |
54
+
55
+ ## Complex Collection Types
56
+
57
+ | Sorbet Type | BAML Output | Example |
58
+ |-------------|-------------|---------|
59
+ | `T::Array[T.any(String, Integer)]` | `(string \| int)[]` | `mixed_array (string \| int)[]` |
60
+ | `T::Hash[String, T.any(String, Integer)]` | `map<string, string \| int>` | `settings map<string, string \| int>` |
61
+ | `T::Hash[String, T::Array[String]]` | `map<string, string[]>` | `labels map<string, string[]>` |
62
+
63
+ ## Structured Types
64
+
65
+ ### T::Struct to BAML Classes (Research Workflow Example)
66
+
67
+ ```ruby
68
+ class ConfidenceLevel < T::Enum
69
+ enums do
70
+ # Low confidence, requires further verification
71
+ Low = new('low')
72
+ # High confidence, strongly supported by evidence
73
+ High = new('high')
74
+ end
75
+ end
76
+
77
+ class ResearchFindings < T::Struct
78
+ # Detailed research findings and analysis
79
+ const :findings, String
80
+ # Key actionable insights extracted
81
+ const :key_insights, T::Array[String]
82
+ # Assessment of evidence quality
83
+ const :evidence_quality, ConfidenceLevel
84
+ # Confidence score (1-10 scale)
85
+ const :confidence_score, Integer
86
+ end
87
+ ```
88
+
89
+ ```ruby
90
+ ResearchFindings.to_baml
91
+ ```
92
+
93
+ **Generated BAML:**
94
+ ```baml
95
+ enum ConfidenceLevel {
96
+ "low" @description("Low confidence, requires further verification")
97
+ "high" @description("High confidence, strongly supported by evidence")
98
+ }
99
+
100
+ class ResearchFindings {
101
+ findings string @description("Detailed research findings and analysis")
102
+ key_insights string[] @description("Key actionable insights extracted")
103
+ evidence_quality ConfidenceLevel @description("Assessment of evidence quality")
104
+ confidence_score int @description("Confidence score (1-10 scale)")
105
+ }
106
+ ```
107
+
108
+ ### T::Enum to BAML Enums (Task Classification)
109
+
110
+ ```ruby
111
+ class TaskType < T::Enum
112
+ enums do
113
+ # Literature review and information gathering
114
+ Research = new('research')
115
+ # Data analysis and statistical interpretation
116
+ Analysis = new('analysis')
117
+ # Combining multiple sources into coherent insights
118
+ Synthesis = new('synthesis')
119
+ end
120
+ end
121
+
122
+ class ResearchTask < T::Struct
123
+ # Clear description of the research objective
124
+ const :objective, String
125
+ # Type of research task to be performed
126
+ const :task_type, TaskType
127
+ end
128
+ ```
129
+
130
+ ```ruby
131
+ [TaskType, ResearchTask].map(&:to_baml).join("\n\n")
132
+ ```
133
+
134
+ **Generated BAML:**
135
+ ```baml
136
+ enum TaskType {
137
+ "research" @description("Literature review and information gathering")
138
+ "analysis" @description("Data analysis and statistical interpretation")
139
+ "synthesis" @description("Combining multiple sources into coherent insights")
140
+ }
141
+
142
+ class ResearchTask {
143
+ objective string @description("Clear description of the research objective")
144
+ task_type TaskType @description("Type of research task to be performed")
145
+ }
146
+ ```
147
+
148
+ ### Complex Real-World Example (Autonomous Research Agent)
149
+
150
+ ```ruby
151
+ class ComplexityLevel < T::Enum
152
+ enums do
153
+ # Basic analysis requiring straightforward research
154
+ Basic = new('basic')
155
+ # Advanced analysis requiring deep domain expertise
156
+ Advanced = new('advanced')
157
+ end
158
+ end
159
+
160
+ class TaskDecomposition < T::Struct
161
+ # The main research topic being investigated
162
+ const :research_topic, String
163
+ # Target complexity level for the decomposition
164
+ const :complexity_level, ComplexityLevel
165
+ # Autonomously generated list of research subtasks
166
+ const :subtasks, T::Array[String]
167
+ # Strategic priority rankings (1-5 scale) for each subtask
168
+ const :priority_order, T::Array[Integer]
169
+ # Task dependency relationships for optimal sequencing
170
+ const :dependencies, T::Array[String]
171
+ # Key-value metadata for agent coordination
172
+ const :agent_metadata, T::Hash[String, T.any(String, Integer)]
173
+ end
174
+ ```
175
+
176
+ ```ruby
177
+ [ComplexityLevel, TaskDecomposition].map(&:to_baml).join("\n\n")
178
+ ```
179
+
180
+ **Generated BAML:**
181
+ ```baml
182
+ enum ComplexityLevel {
183
+ "basic" @description("Basic analysis requiring straightforward research")
184
+ "advanced" @description("Advanced analysis requiring deep domain expertise")
185
+ }
186
+
187
+ class TaskDecomposition {
188
+ research_topic string @description("The main research topic being investigated")
189
+ complexity_level ComplexityLevel @description("Target complexity level for the decomposition")
190
+ subtasks string[] @description("Autonomously generated list of research subtasks")
191
+ priority_order int[] @description("Strategic priority rankings (1-5 scale) for each subtask")
192
+ dependencies string[] @description("Task dependency relationships for optimal sequencing")
193
+ agent_metadata map<string, string | int> @description("Key-value metadata for agent coordination")
194
+ }
195
+ ```
196
+
197
+ ## Advanced Features
198
+
199
+ ### Dependency Management
200
+
201
+ ```ruby
202
+ # Dependencies automatically included with smart defaults
203
+ TaskDecomposition.to_baml
204
+ # Outputs ComplexityLevel enum, then TaskDecomposition class in correct order
205
+ ```
206
+
207
+ ### Field Descriptions (Included by Default)
208
+
209
+ ```ruby
210
+ # Smart defaults extract field descriptions from comments
211
+ TaskDecomposition.to_baml
212
+ # Outputs BAML with @description() annotations for LLM context
213
+ ```
214
+
215
+ ### Custom Formatting
216
+
217
+ ```ruby
218
+ # Smart defaults include dependencies and descriptions automatically
219
+ TaskDecomposition.to_baml(indent_size: 4)
220
+ # Disable features if needed:
221
+ TaskDecomposition.to_baml(include_descriptions: false)
222
+ ```
223
+
224
+ ## ✅ Completed Features
225
+
226
+ - ✅ `T::Struct` → `class Name { ... }` with field descriptions
227
+ - ✅ `T::Enum` → `enum Name { ... }` with value descriptions
228
+ - ✅ Automatic dependency resolution and ordering
229
+ - ✅ Smart defaults (descriptions and dependencies enabled)
230
+ - ✅ Full type safety with Sorbet type checking
231
+
232
+ ## Future Enhancements (Optional)
233
+
234
+ - `T.type_alias` → `type Name = ...`
235
+ - Custom naming strategies (snake_case ↔ camelCase)
236
+ - Self-referential type handling
@@ -0,0 +1,85 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ module.exports = {
3
+ content: [
4
+ "./src/**/*.{html,md,liquid,erb}",
5
+ "./frontend/**/*.js",
6
+ ],
7
+ darkMode: 'media',
8
+ theme: {
9
+ extend: {
10
+ typography: {
11
+ DEFAULT: {
12
+ css: {
13
+ maxWidth: 'none',
14
+ color: 'inherit',
15
+ a: {
16
+ color: '#3b82f6',
17
+ textDecoration: 'underline',
18
+ fontWeight: '500',
19
+ },
20
+ '[class~="lead"]': {
21
+ color: 'inherit',
22
+ },
23
+ strong: {
24
+ color: 'inherit',
25
+ },
26
+ 'ol > li::before': {
27
+ color: 'inherit',
28
+ },
29
+ 'ul > li::before': {
30
+ backgroundColor: 'currentColor',
31
+ },
32
+ hr: {
33
+ borderColor: 'currentColor',
34
+ opacity: 0.3,
35
+ },
36
+ blockquote: {
37
+ color: 'inherit',
38
+ borderLeftColor: 'currentColor',
39
+ opacity: 0.8,
40
+ },
41
+ h1: {
42
+ color: 'inherit',
43
+ },
44
+ h2: {
45
+ color: 'inherit',
46
+ },
47
+ h3: {
48
+ color: 'inherit',
49
+ },
50
+ h4: {
51
+ color: 'inherit',
52
+ },
53
+ 'figure figcaption': {
54
+ color: 'inherit',
55
+ },
56
+ code: {
57
+ color: 'inherit',
58
+ backgroundColor: 'rgba(156, 163, 175, 0.2)',
59
+ padding: '0.125rem 0.25rem',
60
+ borderRadius: '0.25rem',
61
+ fontWeight: '400',
62
+ },
63
+ 'code::before': {
64
+ content: '""',
65
+ },
66
+ 'code::after': {
67
+ content: '""',
68
+ },
69
+ pre: {
70
+ backgroundColor: '#1f2937',
71
+ color: '#f9fafb',
72
+ },
73
+ 'pre code': {
74
+ backgroundColor: 'transparent',
75
+ color: 'inherit',
76
+ },
77
+ },
78
+ },
79
+ },
80
+ },
81
+ },
82
+ plugins: [
83
+ require('@tailwindcss/typography'),
84
+ ],
85
+ }
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+ # typed: false
3
+
4
+ require_relative '../lib/sorbet_baml'
5
+
6
+ puts '🎯 Description Parameter Support Demo'
7
+ puts '=' * 50
8
+
9
+ # Example 1: Basic description parameters
10
+ class User < T::Struct
11
+ const :name, String, description: "User's full legal name"
12
+ prop :age, Integer, description: 'Age in years'
13
+ const :email, T.nilable(String), description: 'Optional email address for notifications'
14
+ const :interests, T::Array[String], description: 'List of user hobbies and interests'
15
+ end
16
+
17
+ puts "\n1. Basic T::Struct with description parameters:"
18
+ puts User.to_baml
19
+
20
+ # Example 2: Mixed description sources (parameters + comments)
21
+ class Product < T::Struct
22
+ # This comment will be used as fallback
23
+ const :id, String
24
+
25
+ const :name, String, description: 'Product name for display'
26
+
27
+ # Price in USD cents
28
+ prop :price_cents, Integer
29
+
30
+ const :category, String, description: 'Product category classification'
31
+ end
32
+
33
+ puts "\n2. Mixed description sources (parameters take priority):"
34
+ puts Product.to_baml
35
+
36
+ # Example 3: Complex nested types with descriptions
37
+ class Order < T::Struct
38
+ const :id, String, description: 'Unique order identifier'
39
+ const :customer, User, description: 'Customer who placed the order'
40
+ const :items, T::Array[Product], description: 'List of ordered products'
41
+ const :total_cents, Integer, description: 'Total order value in USD cents'
42
+ const :status, String, description: 'Current order processing status'
43
+ end
44
+
45
+ puts "\n3. Complex nested types with dependencies:"
46
+ puts Order.to_baml
47
+
48
+ puts "\n✨ Beautiful, readable, and LLM-friendly!"
49
+ puts '🚀 Perfect for DSPy.rb, autonomous agents, and structured LLM outputs'
@@ -8,101 +8,100 @@ module SorbetBaml
8
8
 
9
9
  sig { params(klass: T.class_of(T::Struct)).returns(T::Hash[String, T.nilable(String)]) }
10
10
  def self.extract_field_comments(klass)
11
+ # First try to get descriptions from the description extractor (extra field)
12
+ descriptions = DescriptionExtractor.extract_prop_descriptions(klass)
13
+
14
+ # Then fall back to comment-based extraction for any missing descriptions
11
15
  comments = {}
12
16
  source_file = find_source_file(klass)
13
-
14
- return comments unless source_file && File.exist?(source_file)
15
-
16
- lines = File.readlines(source_file)
17
- extract_comments_from_lines(lines, klass.name.split('::').last, comments)
18
-
19
- comments
17
+
18
+ if source_file && File.exist?(source_file)
19
+ lines = File.readlines(source_file)
20
+ extract_comments_from_lines(lines, T.must(T.must(klass.name).split('::').last), comments)
21
+ end
22
+
23
+ # Merge with priority: description parameters > comments
24
+ descriptions.merge(comments) { |_key, desc_param, _comment| desc_param }
20
25
  end
21
26
 
22
27
  sig { params(klass: T.class_of(T::Enum)).returns(T::Hash[String, T.nilable(String)]) }
23
28
  def self.extract_enum_comments(klass)
24
29
  comments = {}
25
30
  source_file = find_source_file(klass)
26
-
31
+
27
32
  return comments unless source_file && File.exist?(source_file)
28
-
33
+
29
34
  lines = File.readlines(source_file)
30
- extract_enum_comments_from_lines(lines, klass.name.split('::').last, comments)
31
-
35
+ extract_enum_comments_from_lines(lines, T.must(T.must(klass.name).split('::').last), comments)
36
+
32
37
  comments
33
38
  end
34
39
 
35
- private
36
-
37
- sig { params(klass: Class).returns(T.nilable(String)) }
40
+ sig { params(klass: T::Class[T.anything]).returns(T.nilable(String)) }
38
41
  def self.find_source_file(klass)
39
42
  # Try to find where the class was defined
40
43
  # This is a heuristic approach since Ruby doesn't provide reliable source location for classes
41
-
44
+
42
45
  # Method 1: Check if any methods have source location
43
46
  begin
44
47
  if klass.respond_to?(:new) && klass.method(:new).respond_to?(:source_location)
45
48
  location = klass.method(:new).source_location
46
49
  return location[0] if location
47
50
  end
48
- rescue
51
+ rescue StandardError
49
52
  # Ignore errors
50
53
  end
51
-
54
+
52
55
  # Method 2: Look at the current call stack for files that might contain the class
53
56
  caller_locations.each do |location|
54
57
  file_path = location.absolute_path || location.path
55
58
  next unless file_path && File.exist?(file_path)
56
-
59
+
57
60
  # Read the file and check if it contains the class definition
58
61
  begin
59
62
  content = File.read(file_path)
60
- class_name = klass.name.split('::').last
61
- if content.match(/class\s+#{Regexp.escape(class_name)}\s*</)
62
- return file_path
63
- end
64
- rescue
63
+ class_name = T.must(klass.name).split('::').last
64
+ return file_path if content.match(/class\s+#{Regexp.escape(T.must(class_name))}\s*</)
65
+ rescue StandardError
65
66
  # Ignore file read errors
66
67
  end
67
68
  end
68
-
69
+
69
70
  nil
70
71
  end
71
72
 
72
73
  sig { params(lines: T::Array[String], class_name: String, comments: T::Hash[String, T.nilable(String)]).void }
73
74
  def self.extract_comments_from_lines(lines, class_name, comments)
74
- in_target_class = false
75
+ in_target_class = T.let(false, T::Boolean)
75
76
  current_comment = T.let(nil, T.nilable(String))
76
77
  brace_depth = 0
77
-
78
+
78
79
  lines.each do |line|
79
80
  stripped = line.strip
80
-
81
+
81
82
  # Check if we're entering the target class
82
83
  if stripped.match(/^class\s+#{Regexp.escape(class_name)}\s*<\s*T::Struct/)
83
84
  in_target_class = true
84
85
  brace_depth = 0
85
86
  next
86
87
  end
87
-
88
+
88
89
  next unless in_target_class
89
-
90
+
90
91
  # Track brace depth to handle nested classes
91
92
  brace_depth += stripped.count('{')
92
93
  brace_depth -= stripped.count('}')
93
-
94
+
94
95
  # Exit when we reach the end of the class
95
- if stripped == 'end' && brace_depth == 0
96
- break
97
- end
98
-
96
+ break if stripped == 'end' && brace_depth == 0
97
+
99
98
  # Extract comment
100
99
  if stripped.start_with?('#')
101
- comment_text = stripped[1..-1].strip
100
+ comment_text = T.must(stripped[1..-1]).strip
102
101
  current_comment = current_comment ? "#{current_comment} #{comment_text}" : comment_text
103
102
  elsif stripped.match(/^const\s+:(\w+)/) && current_comment
104
- field_name = stripped.match(/^const\s+:(\w+)/)[1]
105
- comments[field_name] = current_comment
103
+ field_name = T.must(stripped.match(/^const\s+:(\w+)/))[1]
104
+ comments[T.must(field_name)] = current_comment
106
105
  current_comment = nil
107
106
  elsif !stripped.empty? && !stripped.start_with?('#')
108
107
  # Reset comment if we hit non-comment, non-const line
@@ -113,47 +112,45 @@ module SorbetBaml
113
112
 
114
113
  sig { params(lines: T::Array[String], class_name: String, comments: T::Hash[String, T.nilable(String)]).void }
115
114
  def self.extract_enum_comments_from_lines(lines, class_name, comments)
116
- in_target_class = false
117
- in_enums_block = false
115
+ in_target_class = T.let(false, T::Boolean)
116
+ in_enums_block = T.let(false, T::Boolean)
118
117
  current_comment = T.let(nil, T.nilable(String))
119
-
118
+
120
119
  lines.each do |line|
121
120
  stripped = line.strip
122
-
121
+
123
122
  # Check if we're entering the target enum class
124
123
  if stripped.match(/^class\s+#{Regexp.escape(class_name)}\s*<\s*T::Enum/)
125
124
  in_target_class = true
126
125
  next
127
126
  end
128
-
127
+
129
128
  next unless in_target_class
130
-
129
+
131
130
  # Check if we're in the enums block
132
131
  if stripped == 'enums do'
133
132
  in_enums_block = true
134
133
  next
135
134
  end
136
-
135
+
137
136
  # Exit enums block
138
137
  if in_enums_block && stripped == 'end'
139
138
  in_enums_block = false
140
139
  next
141
140
  end
142
-
141
+
143
142
  # Exit class
144
- if stripped == 'end' && !in_enums_block
145
- break
146
- end
147
-
143
+ break if stripped == 'end' && !in_enums_block
144
+
148
145
  next unless in_enums_block
149
-
146
+
150
147
  # Extract comment
151
148
  if stripped.start_with?('#')
152
- comment_text = stripped[1..-1].strip
149
+ comment_text = T.must(stripped[1..-1]).strip
153
150
  current_comment = current_comment ? "#{current_comment} #{comment_text}" : comment_text
154
151
  elsif stripped.match(/^(\w+)\s*=\s*new/) && current_comment
155
- enum_name = stripped.match(/^(\w+)\s*=\s*new/)[1]
156
- comments[enum_name] = current_comment
152
+ enum_name = T.must(stripped.match(/^(\w+)\s*=\s*new/))[1]
153
+ comments[T.must(enum_name)] = current_comment
157
154
  current_comment = nil
158
155
  elsif !stripped.empty? && !stripped.start_with?('#')
159
156
  # Reset comment if we hit non-comment, non-enum line
@@ -162,4 +159,4 @@ module SorbetBaml
162
159
  end
163
160
  end
164
161
  end
165
- end
162
+ end