1package url 2 3import ( 4 "fmt" 5) 6 7// Format formats a URL into a human-readable (and reparsable) format. 8func (u *URL) Format(environmentPrefix string) string { 9 if u.Protocol == Protocol_Local { 10 return u.formatLocal() 11 } else if u.Protocol == Protocol_SSH { 12 return u.formatSSH() 13 } else if u.Protocol == Protocol_Tunnel { 14 return u.formatTunnel() 15 } else if u.Protocol == Protocol_Docker { 16 return u.formatDocker(environmentPrefix) 17 } 18 panic("unknown URL protocol") 19} 20 21// formatLocal formats a local URL. 22func (u *URL) formatLocal() string { 23 return u.Path 24} 25 26// formatSSH formats an SSH URL into an SCP-style URL. 27func (u *URL) formatSSH() string { 28 // Create the base result. 29 result := u.Host 30 31 // Add username if present. 32 if u.User != "" { 33 result = fmt.Sprintf("%s@%s", u.User, result) 34 } 35 36 // Add port if present. 37 if u.Port != 0 { 38 result = fmt.Sprintf("%s:%d", result, u.Port) 39 } 40 41 // Add path. 42 result = fmt.Sprintf("%s:%s", result, u.Path) 43 44 // Done. 45 return result 46} 47 48// invalidTunnelURLFormat is the value returned by formatTunnel when a URL is 49// provided that breaks invariants. 50const invalidTunnelURLFormat = "<invalid-tunnel-url>" 51 52// formatTunnel formats a tunnel URL. 53func (u *URL) formatTunnel() string { 54 // Start with the tunnel identifier/name. 55 result := u.Host 56 57 // Ensure that a username is not present 58 if u.User != "" { 59 return invalidTunnelURLFormat 60 } 61 62 // Append the path in a manner that depends on the URL kind. 63 if u.Kind == Kind_Synchronization { 64 // If this is a home-directory-relative path or a Windows path, then we 65 // need to prepend a slash. 66 if u.Path == "" { 67 return invalidTunnelURLFormat 68 } else if u.Path[0] == '/' { 69 result += u.Path 70 } else if u.Path[0] == '~' || isWindowsPath(u.Path) { 71 result += fmt.Sprintf("/%s", u.Path) 72 } else { 73 return invalidTunnelURLFormat 74 } 75 } else if u.Kind == Kind_Forwarding { 76 result += fmt.Sprintf(":%s", u.Path) 77 } else { 78 panic("unhandled URL kind") 79 } 80 81 // Add the scheme. 82 result = tunnelURLPrefix + result 83 84 // Done. 85 return result 86} 87 88// invalidDockerURLFormat is the value returned by formatDocker when a URL is 89// provided that breaks invariants. 90const invalidDockerURLFormat = "<invalid-docker-url>" 91 92// formatDocker formats a Docker URL. 93func (u *URL) formatDocker(environmentPrefix string) string { 94 // Start with the container name. 95 result := u.Host 96 97 // Add username if present. 98 if u.User != "" { 99 result = fmt.Sprintf("%s@%s", u.User, result) 100 } 101 102 // Append the path in a manner that depends on the URL kind. 103 if u.Kind == Kind_Synchronization { 104 // If this is a home-directory-relative path or a Windows path, then we 105 // need to prepend a slash. 106 if u.Path == "" { 107 return invalidDockerURLFormat 108 } else if u.Path[0] == '/' { 109 result += u.Path 110 } else if u.Path[0] == '~' || isWindowsPath(u.Path) { 111 result += fmt.Sprintf("/%s", u.Path) 112 } else { 113 return invalidDockerURLFormat 114 } 115 } else if u.Kind == Kind_Forwarding { 116 result += fmt.Sprintf(":%s", u.Path) 117 } else { 118 panic("unhandled URL kind") 119 } 120 121 // Add the scheme. 122 result = dockerURLPrefix + result 123 124 // Add environment variable information if requested. 125 if environmentPrefix != "" { 126 for _, variable := range DockerEnvironmentVariables { 127 result += fmt.Sprintf("%s%s=%s", environmentPrefix, variable, u.Environment[variable]) 128 } 129 } 130 131 // Done. 132 return result 133} 134