Skip to content

Commit 78d3468

Browse files
harakeishiclaude
andcommitted
ai/refactor(parser): split parsePortString into smaller single-responsibility functions
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent a83351d commit 78d3468

File tree

1 file changed

+28
-34
lines changed

1 file changed

+28
-34
lines changed

internal/parser/yaml.go

Lines changed: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -303,40 +303,21 @@ func expandVariables(input string) string {
303303
return expanded
304304
}
305305

306-
// parsePortString は文字列形式のポートマッピングを解析します。
307-
func (p *YamlComposeParser) parsePortString(ctx context.Context, portStr string) (*types.PortMapping, error) {
308-
// 例: "8080:80", "8080:80/tcp", "127.0.0.1:8080:80", "${PORT:-3000}:80"
309-
310-
// 環境変数を展開
311-
expandedPortStr := expandVariables(portStr)
312-
313-
protocol := "tcp"
314-
portPart := expandedPortStr
306+
// portStringPattern はポート文字列の正規表現パターンです。
307+
// 形式: [host_ip:]host_port:container_port または container_port のみ
308+
var portStringPattern = regexp.MustCompile(`^(?:([\d\.]+):)?(\d+):(\d+)$|^(\d+)$`)
315309

316-
// プロトコル部分を分離
317-
if strings.Contains(expandedPortStr, "/") {
318-
parts := strings.Split(expandedPortStr, "/")
319-
if len(parts) == 2 {
320-
portPart = parts[0]
321-
protocol = parts[1]
322-
}
323-
}
324-
325-
// ポート部分を解析
326-
// 形式: [host_ip:]host_port:container_port または container_port のみ
327-
// IPアドレスは数字とドットで構成される (例: 127.0.0.1:8080:80)
328-
// Note: ホスト名 (例: localhost:8080:80) は Docker Compose の短縮構文では
329-
// サポートされていないため、[\d\.]+ で IP アドレスのみにマッチさせる。
330-
re := regexp.MustCompile(`^(?:([\d\.]+):)?(\d+):(\d+)$|^(\d+)$`)
331-
matches := re.FindStringSubmatch(portPart)
332-
333-
if len(matches) == 0 {
334-
return nil, &errors.AppError{
335-
Code: errors.ErrParseFailed,
336-
Message: fmt.Sprintf("無効なポート形式: %s (展開後: %s)", portStr, expandedPortStr),
337-
}
310+
// extractProtocol はポート文字列からプロトコル部分を分離します。
311+
func extractProtocol(portStr string) (portPart, protocol string) {
312+
if idx := strings.Index(portStr, "/"); idx >= 0 {
313+
return portStr[:idx], portStr[idx+1:]
338314
}
315+
return portStr, "tcp"
316+
}
339317

318+
// buildPortMappingFromRegexMatches は正規表現マッチ結果から PortMapping を構築します。
319+
// matches は portStringPattern.FindStringSubmatch の結果: [full, hostIP, hostPort, containerPort, singlePort]
320+
func buildPortMappingFromRegexMatches(matches []string, protocol string) (*types.PortMapping, error) {
340321
var hostPort, containerPort int
341322
var err error
342323

@@ -350,7 +331,6 @@ func (p *YamlComposeParser) parsePortString(ctx context.Context, portStr string)
350331
Cause: err,
351332
}
352333
}
353-
hostPort = 0 // ホストポートは指定なし
354334
} else {
355335
// ホスト:コンテナ形式(例: "8080:80")
356336
hostPort, err = strconv.Atoi(matches[2])
@@ -361,7 +341,6 @@ func (p *YamlComposeParser) parsePortString(ctx context.Context, portStr string)
361341
Cause: err,
362342
}
363343
}
364-
365344
containerPort, err = strconv.Atoi(matches[3])
366345
if err != nil {
367346
return nil, &errors.AppError{
@@ -378,14 +357,29 @@ func (p *YamlComposeParser) parsePortString(ctx context.Context, portStr string)
378357
Protocol: protocol,
379358
}
380359

381-
// IPアドレスが指定されている場合
382360
if matches[1] != "" {
383361
mapping.HostIP = matches[1]
384362
}
385363

386364
return mapping, nil
387365
}
388366

367+
// parsePortString は文字列形式のポートマッピングを解析します。
368+
func (p *YamlComposeParser) parsePortString(ctx context.Context, portStr string) (*types.PortMapping, error) {
369+
expanded := expandVariables(portStr)
370+
portPart, protocol := extractProtocol(expanded)
371+
372+
matches := portStringPattern.FindStringSubmatch(portPart)
373+
if len(matches) == 0 {
374+
return nil, &errors.AppError{
375+
Code: errors.ErrParseFailed,
376+
Message: fmt.Sprintf("無効なポート形式: %s (展開後: %s)", portStr, expanded),
377+
}
378+
}
379+
380+
return buildPortMappingFromRegexMatches(matches, protocol)
381+
}
382+
389383
// parsePortObject はオブジェクト形式のポートマッピングを解析します。
390384
func (p *YamlComposeParser) parsePortObject(ctx context.Context, portObj map[string]interface{}) (*types.PortMapping, error) {
391385
mapping := &types.PortMapping{

0 commit comments

Comments
 (0)