@@ -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 はオブジェクト形式のポートマッピングを解析します。
390384func (p * YamlComposeParser ) parsePortObject (ctx context.Context , portObj map [string ]interface {}) (* types.PortMapping , error ) {
391385 mapping := & types.PortMapping {
0 commit comments