diff --git a/compiler/test/stdlib/uri.test.gr b/compiler/test/stdlib/uri.test.gr index 20c1b54d1e..90943fc2f1 100644 --- a/compiler/test/stdlib/uri.test.gr +++ b/compiler/test/stdlib/uri.test.gr @@ -3,146 +3,169 @@ module UriTest include "uri" include "result" -record Uri { - scheme: Option, - userinfo: Option, - host: Option, - port: Option, - path: String, - query: Option, - fragment: Option, - string: String, +record ExpectedUri { + expectedScheme: Option, + expectedUserinfo: Option, + expectedHost: Option, + expectedPort: Option, + expectedPath: String, + expectedQuery: Option, + expectedFragment: Option, + expectedString: String, } let default = { - scheme: None, - userinfo: None, - host: None, - port: None, - path: "", - query: None, - fragment: None, - string: "", + expectedScheme: None, + expectedUserinfo: None, + expectedHost: None, + expectedPort: None, + expectedPath: "", + expectedQuery: None, + expectedFragment: None, + expectedString: "", } let testValid = (uriString, expected) => { let uri = Result.unwrap(Uri.parse(uriString)) - assert Uri.scheme(uri) == expected.scheme - assert Uri.userinfo(uri) == expected.userinfo - assert Uri.host(uri) == expected.host - assert Uri.port(uri) == expected.port - assert Uri.path(uri) == expected.path - assert Uri.query(uri) == expected.query - assert Uri.fragment(uri) == expected.fragment + assert uri.scheme == expected.expectedScheme + assert uri.userinfo == expected.expectedUserinfo + assert uri.host == expected.expectedHost + assert uri.port == expected.expectedPort + assert uri.path == expected.expectedPath + assert uri.query == expected.expectedQuery + assert uri.fragment == expected.expectedFragment assert Uri.toString(uri) == - (if (expected.string == "") uriString else expected.string) + (if (expected.expectedString == "") uriString else expected.expectedString) } testValid( "https://grain-lang.org", - { ...default, scheme: Some("https"), host: Some("grain-lang.org") } + { + ...default, + expectedScheme: Some("https"), + expectedHost: Some("grain-lang.org"), + } ) testValid( "http://user:password@www.domain.com:80/path/inner?q1=v1&q2=v2#frag", { ...default, - scheme: Some("http"), - userinfo: Some("user:password"), - host: Some("www.domain.com"), - port: Some(80), - path: "/path/inner", - query: Some("q1=v1&q2=v2"), - fragment: Some("frag"), + expectedScheme: Some("http"), + expectedUserinfo: Some("user:password"), + expectedHost: Some("www.domain.com"), + expectedPort: Some(80), + expectedPath: "/path/inner", + expectedQuery: Some("q1=v1&q2=v2"), + expectedFragment: Some("frag"), } ) testValid( "http://www.domain.com:80/path?q1=v1/?q2=v2#frag/?", { ...default, - scheme: Some("http"), - host: Some("www.domain.com"), - port: Some(80), - path: "/path", - query: Some("q1=v1/?q2=v2"), - fragment: Some("frag/?"), + expectedScheme: Some("http"), + expectedHost: Some("www.domain.com"), + expectedPort: Some(80), + expectedPath: "/path", + expectedQuery: Some("q1=v1/?q2=v2"), + expectedFragment: Some("frag/?"), } ) testValid( "a12+3-4.5://1a-._~%1f%Fa!$&'()*+,;=:@0.99.100.255://?1%1f@:/?#/?a", { ...default, - scheme: Some("a12+3-4.5"), - userinfo: Some( + expectedScheme: Some("a12+3-4.5"), + expectedUserinfo: Some( "1a-._~%1f%Fa!$&'()*+,;=:" ), // Do not turn %1f into %1F in userinfo - host: Some("0.99.100.255"), - path: "//", - query: Some("1%1F@:/?"), - fragment: Some("/?a"), - string: "a12+3-4.5://1a-._~%1f%Fa!$&'()*+,;=:@0.99.100.255//?1%1F@:/?#/?a", + expectedHost: Some("0.99.100.255"), + expectedPath: "//", + expectedQuery: Some("1%1F@:/?"), + expectedFragment: Some("/?a"), + expectedString: "a12+3-4.5://1a-._~%1f%Fa!$&'()*+,;=:@0.99.100.255//?1%1F@:/?#/?a", } ) testValid( "mailto:me@email.com", - { ...default, scheme: Some("mailto"), path: "me@email.com" } + { ...default, expectedScheme: Some("mailto"), expectedPath: "me@email.com" } ) testValid( "urn:hello:world", - { ...default, scheme: Some("urn"), path: "hello:world" } + { ...default, expectedScheme: Some("urn"), expectedPath: "hello:world" } ) testValid( "tel:+1-888-888-8888", - { ...default, scheme: Some("tel"), path: "+1-888-888-8888" } + { ...default, expectedScheme: Some("tel"), expectedPath: "+1-888-888-8888" } +) +testValid( + "scheme:/", + { ...default, expectedScheme: Some("scheme"), expectedPath: "/" } +) +testValid( + "scheme://", + { ...default, expectedScheme: Some("scheme"), expectedHost: Some("") } ) -testValid("scheme:/", { ...default, scheme: Some("scheme"), path: "/" }) -testValid("scheme://", { ...default, scheme: Some("scheme"), host: Some("") }) testValid( "scheme:///", - { ...default, scheme: Some("scheme"), host: Some(""), path: "/" } + { + ...default, + expectedScheme: Some("scheme"), + expectedHost: Some(""), + expectedPath: "/", + } ) testValid( "ScHeMe://HoSt%2a.COM/Path", { ...default, - scheme: Some("scheme"), - host: Some("host%2A.com"), - path: "/Path", - string: "scheme://host%2A.com/Path", + expectedScheme: Some("scheme"), + expectedHost: Some("host%2A.com"), + expectedPath: "/Path", + expectedString: "scheme://host%2A.com/Path", } ) testValid( "scheme://%41:%61@%48%65%6c%6C%6f/%25%7e%68%69", { ...default, - scheme: Some("scheme"), - userinfo: Some("%41:%61"), - host: Some("hello"), - path: "/%25~hi", - string: "scheme://%41:%61@hello/%25~hi", + expectedScheme: Some("scheme"), + expectedUserinfo: Some("%41:%61"), + expectedHost: Some("hello"), + expectedPath: "/%25~hi", + expectedString: "scheme://%41:%61@hello/%25~hi", } ) -testValid("scheme:", { ...default, scheme: Some("scheme") }) +testValid("scheme:", { ...default, expectedScheme: Some("scheme") }) testValid( "http://:80", - { ...default, scheme: Some("http"), host: Some(""), port: Some(80) } + { + ...default, + expectedScheme: Some("http"), + expectedHost: Some(""), + expectedPort: Some(80), + } +) +testValid("google.com", { ...default, expectedPath: "google.com" }) +testValid( + "//google.com/", + { ...default, expectedHost: Some("google.com"), expectedPath: "/" } ) -testValid("google.com", { ...default, path: "google.com" }) -testValid("//google.com/", { ...default, host: Some("google.com"), path: "/" }) testValid("", default) testValid( "google.com:80", - { ...default, scheme: Some("google.com"), path: "80" } + { ...default, expectedScheme: Some("google.com"), expectedPath: "80" } ) -testValid(".././..", { ...default, path: ".././.." }) +testValid(".././..", { ...default, expectedPath: ".././.." }) testValid( "http://?#", { ...default, - scheme: Some("http"), - host: Some(""), - path: "", - query: Some(""), - fragment: Some(""), + expectedScheme: Some("http"), + expectedHost: Some(""), + expectedPath: "", + expectedQuery: Some(""), + expectedFragment: Some(""), } ) @@ -164,9 +187,9 @@ let testHostValid = (host, parsed="") => { "scheme://" ++ host, { ...default, - scheme: Some("scheme"), - host: Some(parsed), - string: "scheme://" ++ parsed, + expectedScheme: Some("scheme"), + expectedHost: Some(parsed), + expectedString: "scheme://" ++ parsed, } ) } @@ -204,10 +227,10 @@ let testPath = (path, expected) => { "scheme://domain" ++ path, { ...default, - scheme: Some("scheme"), - host: Some("domain"), - path: expected, - string: "scheme://domain" ++ expected, + expectedScheme: Some("scheme"), + expectedHost: Some("domain"), + expectedPath: expected, + expectedString: "scheme://domain" ++ expected, } ) } diff --git a/stdlib/uri.gr b/stdlib/uri.gr index 9cf1d92c69..b8f8d3a1a7 100644 --- a/stdlib/uri.gr +++ b/stdlib/uri.gr @@ -23,7 +23,7 @@ include "result" /** * Represents a parsed RFC 3986 URI. */ -abstract record Uri { +provide record Uri { scheme: Option, userinfo: Option, host: Option, @@ -734,7 +734,7 @@ provide let parse = str => { * Transforms a base URI and a URI reference into a target URI * * @param base: The base URI to resolve a URI reference on - * @param rel: The URI reference to apply onto the base + * @param ref: The URI reference to apply onto the base * @returns `Ok(uri)` containing the target `Uri`, or `Err(err)` if input is malformed * * @example resolveReference(unwrap(parse("https://grain-lang.org/docs/stdlib/uri")), unwrap(parse("../intro"))) // https://grain-lang.org/docs/intro @@ -1057,76 +1057,6 @@ provide let update = return Ok({ scheme, userinfo, host, port, path, query, fragment }) } -/** - * Retrieves the scheme component of a `Uri`, if it has one - * - * @param uri: The `Uri` to get the scheme of - * @returns `Some(scheme)` containing the scheme of the `Uri`, or `None` if the `Uri` is a relative reference - * - * @since v0.6.0 - */ -provide let scheme = uri => uri.scheme - -/** - * Retrieves the userinfo component of a `Uri`, if it has one - * - * @param uri: The `Uri` to get the userinfo of - * @returns `Some(userinfo)` containing the userinfo of the `Uri`, or `None` if the `Uri` does not have one - * - * @since v0.6.0 - */ -provide let userinfo = uri => uri.userinfo - -/** - * Retrieves the host component of a `Uri` - * - * @param uri: The `Uri` to get the host of - * @returns `Some(host)` containing the host of the `Uri`, or `None` if the `Uri` does not have one - * - * @since v0.6.0 - */ -provide let host = uri => uri.host - -/** - * Retrieves the port component of a `Uri`, if it has one - * - * @param uri: The `Uri` to get the port of - * @returns `Some(port)` containing the port of the `Uri`, or `None` if the `Uri` is a relative reference - * - * @since v0.6.0 - */ -provide let port = uri => uri.port - -/** - * Retrieves the path component of a `Uri` - * - * @param uri: The `Uri` to get the path of - * @returns The path of the given `Uri` - * - * @since v0.6.0 - */ -provide let path = uri => uri.path - -/** - * Retrieves the query string component of a `Uri`, if it has one - * - * @param uri: The `Uri` to get the query string of - * @returns `Some(query)` containing the query string of the `Uri`, or `None` if the `Uri` does not have one - * - * @since v0.6.0 - */ -provide let query = uri => uri.query - -/** - * Retrieves the fragment component of a `Uri`, if it has one - * - * @param uri: The `Uri` to get the fragment of - * @returns `Some(fragment)` containing the fragment of the `Uri`, or `None` if the `Uri` does not have one - * - * @since v0.6.0 - */ -provide let fragment = uri => uri.fragment - /** * Determines whether or not a `Uri` has an authority (i.e. has a host component) * diff --git a/stdlib/uri.md b/stdlib/uri.md index 53086bc553..6e1102b348 100644 --- a/stdlib/uri.md +++ b/stdlib/uri.md @@ -20,7 +20,15 @@ Type declarations included in the Uri module. ### Uri.**Uri** ```grain -type Uri +record Uri { + scheme: Option, + userinfo: Option, + host: Option, + port: Option, + path: String, + query: Option, + fragment: Option, +} ``` Represents a parsed RFC 3986 URI. @@ -111,7 +119,7 @@ Parameters: |param|type|description| |-----|----|-----------| |`str`|`String`|The string to encode| -|`encodeSet`|`Option`|An indication for which characters to percent-encode. `EncodeNonUnreserved` by default| +|`?encodeSet`|`PercentEncodeSet`|An indication for which characters to percent-encode. `EncodeNonUnreserved` by default| Returns: @@ -166,7 +174,8 @@ No other changes yet. ```grain -encodeQuery : (urlVals: List<(String, String)>) => String +encodeQuery : + (urlVals: List<(String, String)>, ?encodeSet: PercentEncodeSet) => String ``` Encodes a list of key-value pairs into an query string. @@ -265,7 +274,7 @@ Parameters: |param|type|description| |-----|----|-----------| |`base`|`Uri`|The base URI to resolve a URI reference on| -|`rel`|`Uri`|The URI reference to apply onto the base| +|`ref`|`Uri`|The URI reference to apply onto the base| Returns: @@ -308,14 +317,14 @@ Parameters: |param|type|description| |-----|----|-----------| -|`scheme`|`Option>`|`Some(scheme)` containing the desired scheme component or `None` for a scheme-less URI| -|`userinfo`|`Option>`|`Some(userinfo)` containing the desired userinfo component or `None` for a userinfo-less URI| -|`host`|`Option>`|`Some(host)` containing the desired host component or `None` for a host-less URI| -|`port`|`Option>`|`Some(port)` containing the desired port component or `None` for a port-less URI| -|`path`|`Option`|The desired path for the URI. `""` by default| -|`query`|`Option>`|`Some(query)` containing the desired query string component or `None` for a query-less URI| -|`fragment`|`Option>`|`Some(fragment)` containing the desired fragment component or `None` for a fragment-less URI| -|`percentEncodeComponents`|`Option`|Whether or not to apply percent encoding for each component to remove unsafe characters for each component| +|`?scheme`|`Option`|`Some(scheme)` containing the desired scheme component or `None` for a scheme-less URI| +|`?userinfo`|`Option`|`Some(userinfo)` containing the desired userinfo component or `None` for a userinfo-less URI| +|`?host`|`Option`|`Some(host)` containing the desired host component or `None` for a host-less URI| +|`?port`|`Option`|`Some(port)` containing the desired port component or `None` for a port-less URI| +|`?path`|`String`|The desired path for the URI. `""` by default| +|`?query`|`Option`|`Some(query)` containing the desired query string component or `None` for a query-less URI| +|`?fragment`|`Option`|`Some(fragment)` containing the desired fragment component or `None` for a fragment-less URI| +|`?percentEncodeComponents`|`Bool`|Whether or not to apply percent encoding for each component to remove unsafe characters for each component| Examples: @@ -361,14 +370,14 @@ Parameters: |param|type|description| |-----|----|-----------| |`uri`|`Uri`|The base `Uri` to apply updates on top of| -|`scheme`|`Option>>`|`Some(scheme)` containing the desired updated scheme component or `None` to maintain the base URI's scheme| -|`userinfo`|`Option>>`|`Some(userinfo)` containing the desired updated userinfo component or `None` to maintain the base URI's userinfo| -|`host`|`Option>>`|`Some(host)` containing the desired updated host component or `None` to maintain the base URI's host| -|`port`|`Option>>`|`Some(port)` containing the desired updated port component or `None` to maintain the base URI's port| -|`path`|`Option>`|`Some(path)` containing the desired updated path component or `None` to maintain the base URI's path| -|`query`|`Option>>`|`Some(query)` containing the desired updated query string component or `None` to maintain the base URI's query| -|`fragment`|`Option>>`|`Some(fragment)` containing the desired updated fragment component or `None` to maintain the base URI's fragment| -|`percentEncodeComponents`|`Option`|Whether or not to apply percent encoding for each updated component to remove unsafe characters| +|`?scheme`|`Option>`|`Some(scheme)` containing the desired updated scheme component or `None` to maintain the base URI's scheme| +|`?userinfo`|`Option>`|`Some(userinfo)` containing the desired updated userinfo component or `None` to maintain the base URI's userinfo| +|`?host`|`Option>`|`Some(host)` containing the desired updated host component or `None` to maintain the base URI's host| +|`?port`|`Option>`|`Some(port)` containing the desired updated port component or `None` to maintain the base URI's port| +|`?path`|`Option`|`Some(path)` containing the desired updated path component or `None` to maintain the base URI's path| +|`?query`|`Option>`|`Some(query)` containing the desired updated query string component or `None` to maintain the base URI's query| +|`?fragment`|`Option>`|`Some(fragment)` containing the desired updated fragment component or `None` to maintain the base URI's fragment| +|`?percentEncodeComponents`|`Bool`|Whether or not to apply percent encoding for each updated component to remove unsafe characters| Examples: @@ -392,181 +401,6 @@ Uri.update(uri, host=Some(Some("g/r@in")), percentEncodeComponents=true) // http Uri.update(uri, host=Some(None), port=Some(Some(80))) // Err(Uri.PortWithNoHost) ``` -### Uri.**scheme** - -
-Added in next -No other changes yet. -
- -```grain -scheme : (uri: Uri) => Option -``` - -Retrieves the scheme component of a `Uri`, if it has one - -Parameters: - -|param|type|description| -|-----|----|-----------| -|`uri`|`Uri`|The `Uri` to get the scheme of| - -Returns: - -|type|description| -|----|-----------| -|`Option`|`Some(scheme)` containing the scheme of the `Uri`, or `None` if the `Uri` is a relative reference| - -### Uri.**userinfo** - -
-Added in next -No other changes yet. -
- -```grain -userinfo : (uri: Uri) => Option -``` - -Retrieves the userinfo component of a `Uri`, if it has one - -Parameters: - -|param|type|description| -|-----|----|-----------| -|`uri`|`Uri`|The `Uri` to get the userinfo of| - -Returns: - -|type|description| -|----|-----------| -|`Option`|`Some(userinfo)` containing the userinfo of the `Uri`, or `None` if the `Uri` does not have one| - -### Uri.**host** - -
-Added in next -No other changes yet. -
- -```grain -host : (uri: Uri) => Option -``` - -Retrieves the host component of a `Uri` - -Parameters: - -|param|type|description| -|-----|----|-----------| -|`uri`|`Uri`|The `Uri` to get the host of| - -Returns: - -|type|description| -|----|-----------| -|`Option`|`Some(host)` containing the host of the `Uri`, or `None` if the `Uri` does not have one| - -### Uri.**port** - -
-Added in next -No other changes yet. -
- -```grain -port : (uri: Uri) => Option -``` - -Retrieves the port component of a `Uri`, if it has one - -Parameters: - -|param|type|description| -|-----|----|-----------| -|`uri`|`Uri`|The `Uri` to get the port of| - -Returns: - -|type|description| -|----|-----------| -|`Option`|`Some(port)` containing the port of the `Uri`, or `None` if the `Uri` is a relative reference| - -### Uri.**path** - -
-Added in next -No other changes yet. -
- -```grain -path : (uri: Uri) => String -``` - -Retrieves the path component of a `Uri` - -Parameters: - -|param|type|description| -|-----|----|-----------| -|`uri`|`Uri`|The `Uri` to get the path of| - -Returns: - -|type|description| -|----|-----------| -|`String`|The path of the given `Uri`| - -### Uri.**query** - -
-Added in next -No other changes yet. -
- -```grain -query : (uri: Uri) => Option -``` - -Retrieves the query string component of a `Uri`, if it has one - -Parameters: - -|param|type|description| -|-----|----|-----------| -|`uri`|`Uri`|The `Uri` to get the query string of| - -Returns: - -|type|description| -|----|-----------| -|`Option`|`Some(query)` containing the query string of the `Uri`, or `None` if the `Uri` does not have one| - -### Uri.**fragment** - -
-Added in next -No other changes yet. -
- -```grain -fragment : (uri: Uri) => Option -``` - -Retrieves the fragment component of a `Uri`, if it has one - -Parameters: - -|param|type|description| -|-----|----|-----------| -|`uri`|`Uri`|The `Uri` to get the fragment of| - -Returns: - -|type|description| -|----|-----------| -|`Option`|`Some(fragment)` containing the fragment of the `Uri`, or `None` if the `Uri` does not have one| - ### Uri.**hasAuthority**