(p string)
| 1553 | } |
| 1554 | |
| 1555 | func ParseGRPCPath(p string) (serviceFull, method string, err error) { |
| 1556 | // Trim whitespace and validate input |
| 1557 | p = strings.TrimSpace(p) |
| 1558 | if p == "" { |
| 1559 | return "", "", fmt.Errorf("gRPC path cannot be empty") |
| 1560 | } |
| 1561 | |
| 1562 | // Store original path for error messages |
| 1563 | originalPath := p |
| 1564 | p = strings.TrimPrefix(p, "/") |
| 1565 | |
| 1566 | // Split path into components |
| 1567 | parts := strings.Split(p, "/") |
| 1568 | if len(parts) != 2 { |
| 1569 | return "", "", fmt.Errorf("invalid gRPC path %q: expected format '/package.Service/Method', got %d path segments", originalPath, len(parts)) |
| 1570 | } |
| 1571 | |
| 1572 | serviceFull = strings.TrimSpace(parts[0]) |
| 1573 | method = strings.TrimSpace(parts[1]) |
| 1574 | |
| 1575 | // Validate service name is not empty |
| 1576 | if serviceFull == "" { |
| 1577 | return "", "", fmt.Errorf("invalid gRPC path %q: service name cannot be empty", originalPath) |
| 1578 | } |
| 1579 | |
| 1580 | // Validate method name is not empty |
| 1581 | if method == "" { |
| 1582 | return "", "", fmt.Errorf("invalid gRPC path %q: method name cannot be empty", originalPath) |
| 1583 | } |
| 1584 | |
| 1585 | // Validate service name format (should contain at least package.Service) |
| 1586 | if !strings.Contains(serviceFull, ".") { |
| 1587 | return "", "", fmt.Errorf("invalid service name %q in path %q: expected format 'package.Service'", serviceFull, originalPath) |
| 1588 | } |
| 1589 | |
| 1590 | // Validate service name doesn't start or end with dots |
| 1591 | if strings.HasPrefix(serviceFull, ".") || strings.HasSuffix(serviceFull, ".") { |
| 1592 | return "", "", fmt.Errorf("invalid service name %q in path %q: cannot start or end with '.'", serviceFull, originalPath) |
| 1593 | } |
| 1594 | |
| 1595 | // Validate service name doesn't contain consecutive dots |
| 1596 | if strings.Contains(serviceFull, "..") { |
| 1597 | return "", "", fmt.Errorf("invalid service name %q in path %q: cannot contain consecutive dots", serviceFull, originalPath) |
| 1598 | } |
| 1599 | |
| 1600 | // Validate method name format (basic identifier validation) |
| 1601 | if !isValidGRPCIdentifier(method) { |
| 1602 | return "", "", fmt.Errorf("invalid method name %q in path %q: must be a valid identifier", method, originalPath) |
| 1603 | } |
| 1604 | |
| 1605 | // Validate service parts are valid identifiers |
| 1606 | serviceParts := strings.Split(serviceFull, ".") |
| 1607 | for i, part := range serviceParts { |
| 1608 | if !isValidGRPCIdentifier(part) { |
| 1609 | return "", "", fmt.Errorf("invalid service name component %q at position %d in path %q: must be a valid identifier", part, i, originalPath) |
| 1610 | } |
| 1611 | } |
| 1612 |
no test coverage detected