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