Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added better overview, examples, and links #52612

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
131 changes: 95 additions & 36 deletions sdk/lib/core/uri.dart
Expand Up @@ -24,24 +24,52 @@ const int _LOWER_CASE_Z = 0x7A;

const String _hexDigits = "0123456789ABCDEF";

/// A parsed URI, such as a URL.
/// A parsed URI, such as a URL. URI is Uniform Resource Identifier. URL is
/// Uniform Resource Locator, a.k.a. a web address.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First paragraph of a DartDoc should be a single one-line sentence.

I'd move the added sentences to a paragrpah of their own. Hmm, maybe with links to definitions.

Or include URI

/// A parsed Uniform Resource Identifier (URI), such as a URL.
///
/// A URL, Uniform Resource Locator, is for example a web address or
/// local file path.
/// A URL expresses *how* to access a resource. All URLs are URIs.
///
/// A [URI][rfc3986] is an resource *identifier*, which means it may uniquely define
/// something without necessarily providing a way to access that thing
/// from the URI. 
/// One example is a mail-link like `mailto:someone@example.com`, which
/// identifies a mailbox, but doesn't provide a way to access it.
/// Most URIs being used are URLs, the most common ones being URIs with the
/// `http`, `https` and `file` schemes.
///
/// [rfc3986]: https://datatracker.ietf.org/doc/html/rfc3986 "RFC 3986"

///
/// To create a URI with specific components, use [new Uri]:
/// Use this class to represent web addresses (`https://...`), local file
/// locations (`C:\file` on Windows or `/file` on Unixes), or local folder
/// locations (`C:\folder\` or `/folder/`).
///
/// You can create web URIs either by specifying the scheme (protocol) as a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(We generally don't address the reader in DartDocs. I'm not sure that's always the right choice, sometimes it makes the phrasing more convoluted, but it is a style that's farily consistent.)

/// parameter (`Uri(scheme: 'https', ...)`) or using the optional [Uri.https]
/// or [Uri.http] constructor.
///
/// To create a URI, use [new Uri]:
/// ```dart
/// var httpsUri = Uri(
/// // Use a final if the Uri contents never change
/// final dartLibTour = Uri(
/// scheme: 'https',
/// host: 'dart.dev',
/// path: '/guides/libraries/library-tour',
/// fragment: 'numbers');
/// print(httpsUri); // https://dart.dev/guides/libraries/library-tour#numbers
/// print(dartLibTour); // https://dart.dev/guides/libraries/library-tour#numbers
/// ```
///
/// httpsUri = Uri(
/// The optional parts of the URL at the end come in two flavors: they can
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth introducing the format of a hierarchical URI (which this class is generally representing),
before talking about the optional parts at the end.

/// A hierarchical URI has the following format:
/// ```
/// scheme://username:password@host:port/path?query#fragment
/// ```
/// where all parts are optional. 

If that precedes the "To create a URI", it explains the names of the parameters of Uri.

/// be fragments or query parameters. For convenience, the query Params
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't mention the fragment here. Just:

/// The _query_ part of a URI traditionally contains encoded key/value pairs, 
/// originally the values of an HTML form. For convenience, the query 
/// can be provided as `queryParameters`, a `Map` from parameter names to their values, 
/// which are either a single string or an `Iterable<String>` when the same name has
/// multiple values.
///
/// For example this code uses the `queryParameters`:
/// ```
/// ...
/// ```

/// can be provided in the form of a query string, or piecemeal in the form
/// of a map of query parameter names and their values.
///
/// For example, using `queryParameters` instead of the `query` `String?`:
///
/// ```dart
/// // Use a var if you want to change the Uri later
/// var exampleUri = Uri(scheme: 'https', host: 'dart.dev');
/// print(exampleUri); // https://dart.dev
/// // uh oh, that's not what we wanted...let's change it:
/// // https://en.wikipedia.org/w/index.php?title=Uniform_Resource_Identifier&action=history
/// exampleUri = Uri(
/// scheme: 'https',
/// host: 'example.com',
/// path: '/page/',
/// queryParameters: {'search': 'blue', 'limit': '10'});
/// print(httpsUri); // https://example.com/page/?search=blue&limit=10
/// host: 'wikipedia.org',
/// path: '/w/index.php',
/// queryParameters: {'title': 'Uniform_Resource_Identifier', 'action':'history'});
/// print(exampleUri); // https://en.wikipedia.org/w/index.php?title=Uniform_Resource_Identifier&action=history
/// ```
///
/// Make a mailto link to email someone in the user's default Mail application:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think mentioning a "default Mail application" is useful here. Maybe as an example, but that still presumes that the code runs in a setting where there is a default mail application.

///
/// ```dart
/// final mailtoUri = Uri(
/// scheme: 'mailto',
/// path: 'John.Doe@example.com',
Expand All @@ -50,14 +78,22 @@ const String _hexDigits = "0123456789ABCDEF";
/// ```
///
/// ## HTTP and HTTPS URI
/// To create a URI with https scheme, use [Uri.https] or [Uri.http]:
/// As mentioned before, these are special constructors that do the same thing
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think they have been mentioned before. Also it's not clear which constructors are referenced here, since they haven't been mentioned before either.

Something like:

/// The [Uri.http] and [Uri.https] constructors are specialized for creating
/// URIs with the `http:` and `https:` schemes, and doing so more conveniently
/// than through the general [new Uri] constructor.

/// as the standard constructor, but with less typing involved.
/// To create a URI with https scheme, use [Uri.https]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https -> `https:`

/// ```dart
/// final httpsUri = Uri.https('example.com', 'api/fetch', {'limit': '10'});
/// print(httpsUri); // https://example.com/api/fetch?limit=10
/// ```
///
/// For the old http scheme, use [Uri.http]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is nothing old about http. Just drop the "old" here.

///
/// ## File URI
/// To create a URI from file path, use [Uri.file]:
/// To create a URI for a file path, use [Uri.file]:
/// ```dart
/// // for your convenience, use r before the string to deactivate
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not the best place to explain raw strings in detail. It's OK to mention them briefly, and link to [String] instead for details.
Maybe:

/// (Uses raw strings ([String]) for paths to avoid the `\`s of Windows paths being
/// interpreted as a string escape sequence.)

/// // the Dart interpreter within the string. The r means it's
/// // a "raw" string.
/// final fileUriUnix =
/// Uri.file(r'/home/myself/images/image.png', windows: false);
/// print(fileUriUnix); // file:///home/myself/images/image.png
Expand Down Expand Up @@ -95,7 +131,11 @@ const String _hexDigits = "0123456789ABCDEF";
/// print(uri.fragment); // utility-classes
/// print(uri.hasQuery); // false
/// print(uri.data); // null
/// ```
/// ```
///
/// Please note that this performs the parsing at runtime, which slows
/// down your application, as opposed to the explicit declaration
/// using one of the other constructors.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.... well, actually ...
It's true that parsing has a cost, but every other constructor also does some parsing and normalization.

If the URI is particularly simple (nothing needs to be escaped or unescaped, simple stuff like https://example.com/banana/hut.html which most actual URIs are, it is probably more efficient to use Uri.parse, because it just scans the string once, decides that it is "simple", and stores it as-is.

The other constructors require multiple strings as input, will check each for whether to escape or not, and will then remember all the individual parts. I think it caches the concatenation if you call toString, so it also takes more memory.

TL;DR: Parsing simple URIs is probably the most efficient way to create a URI.

In general, users should not worry about performance of Uri, they should do what is best for the program logic. If they end up URI being a bottleneck, then they can start looking for optimizations. Most programs do many other things than parsing URIs.

///
/// **See also:**
/// * [URIs][uris] in the [library tour][libtour]
Expand Down Expand Up @@ -213,7 +253,7 @@ abstract interface class Uri {
Map<String, dynamic /*String?|Iterable<String>*/ >? queryParameters,
String? fragment}) = _Uri;

/// Creates a new `http` URI from authority, path and query.
/// Creates a new `http` URI from authority (hostname), path and query.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be more than hostname. Maybe (usually a hostname) instead of just (hostname).
Same below.

///
/// Example:
/// ```dart
Expand Down Expand Up @@ -250,7 +290,7 @@ abstract interface class Uri {
Map<String, dynamic /*String?|Iterable<String>*/ >? queryParameters,
]) = _Uri.http;

/// Creates a new `https` URI from authority, path and query.
/// Creates a new `https` URI from authority (hostname), path and query.
///
/// This constructor is the same as [Uri.http] except for the scheme
/// which is set to `https`.
Expand Down Expand Up @@ -289,7 +329,7 @@ abstract interface class Uri {
/// only backslash (`\`) separates path segments in [path].
///
/// If the path starts with a path separator, an absolute URI (with the
/// `file` scheme and an empty authority) is created.
/// `file` scheme and an empty authority (hostname)) is created.
/// Otherwise a relative URI reference with no scheme or authority is created.
/// One exception to this rule is that when Windows semantics is used
/// and the path starts with a drive letter followed by a colon (":") and a
Expand Down Expand Up @@ -459,21 +499,23 @@ abstract interface class Uri {
/// The returned scheme is canonicalized to lowercase letters.
String get scheme;

/// The authority component.
/// The authority (hostname) component.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't add the (hostname) comment here at all.

This is the authority accessor, which explicitly accesses more than just .host.

///
/// The authority is formatted from the [userInfo], [host] and [port]
/// parts.
/// The authority is the [hostname] + [port]. Optionally includes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first sentence is technically incorrect. I prefer to not say anything that is technically incorrect, even if it is corrected in the very next sentence, because not all readers will read the next sentence.

So, something like:

/// The authority always incldes the [host], which can be either a host name,
/// an IPv4 address or an IPv6 address. The host optionally be followed by
/// a [port] number, and preceded by [userInfo], for example:
/// * `//example.com`
/// * `//username:password@example.com:8080`
/// * `//127.0.0.1:80` (IPv4 loopback)
/// * `//[::1]:80/` (IPv6 loopback)

I use the // before every example, because it is part of the authority.

/// user credential ([userInfo]) used to login to the machine.
/// It is formatted as [userInfo]@[hostname]:[port]
/// Example: leetHaxor@google.com:1337
///
/// The value is the empty string if there is no authority component.
/// If there is no authority component, it is an empty string.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

   /// If this URI has no authority component ([hasAuthority]), 
   /// this value is the empty string.
   /// Otherwise it is a string starting with `"//"`.

String get authority;

/// The user info part of the authority component.
/// The user info part of the [authority] (hostname) component.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the (hostname) comment here.
Same for host.

///
/// The value is the empty string if there is no user info in the
/// authority component.
String get userInfo;

/// The host part of the authority component.
/// The host part of the [authority] (hostname) component.
///
/// The value is the empty string if there is no authority component and
/// hence no host.
Expand All @@ -486,7 +528,7 @@ abstract interface class Uri {
/// with upper-case percent-escapes.
String get host;

/// The port part of the authority component.
/// The port part of the [authority] component.
///
/// The value is the default port if there is no port number in the authority
/// component. That's 80 for http, 443 for https, and 0 for everything else.
Expand All @@ -512,21 +554,38 @@ abstract interface class Uri {

/// The fragment identifier component.
///
/// A fragment identifier is a string after URI, after the hash, which
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd go with something like:

/// A fragment identifier is the part of a URI after a `#` character.
/// It is intended to indentify something specific to the document.
/// In a link to a web page, the fragment traditionally identifies a section
/// or part of the page by its HTML `id` attribute.
/// In modern browsers, it can also identify a part of the page to be
/// recognized by the browser, for example a specific text to find and
/// show to the user.

/// identifies something specific as a function of the document. For
/// a user interface Web document such as HTML page, it traditionally
/// identifies a section or part of the page. In modern pages, it can
/// indicate a particular view of the page.
///
/// The value is the empty string if there is no fragment identifier
/// component.
String get fragment;

/// The URI path split into its segments.
/// Same as the URI [path], but split into its segments.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it's not the same. I'd use the original phrasing, but add the [path] link.

///
/// This is an alternative way to access the [path] component and
/// is useful for when a program needs to access components of a URI
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

access components of a URI -> work with the segments of a URI path

/// such as the immediate parent folder of the current file.
/// This kind of field is valuable for extracting folder names
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd drop the "kind of field", it's not clear what it means. The folder names was mentioned jsut above.

So maybe just

  /// Where the [path] contains the URIs path component, including any escapes,
  /// the individual segments of `pathSegments` have been unescaped and can,
  /// for example, easily be displayed in a breadcrumb display.
I'd proably just drop it. I wouldn'

/// for display in a GUI's breadcrumb navigation component.
///
/// For example, if iterating through files in several local folders
/// that have a similar folder and file naming scheme, you could modify
/// `pathSegments` to hit every file that matches a pattern within every
/// folder that matches a pattern.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that paragraph is too vague to be useful.

Giving lots of examples of ways to use a value means attempting to guess the user's needs.
If we guess wrong, it's just noise to the reader, useless examples for something they're not doing.

Each example should show something generally useful

///
/// Each of the segments in the list has been decoded.
/// Each of the segments in the list is already decoded.
/// If the path is empty, the empty list will
/// be returned. A leading slash `/` does not affect the segments returned.
///
/// The list is unmodifiable and will throw [UnsupportedError] on any
/// calls that would mutate it.
List<String> get pathSegments;

/// The URI query split into a map according to the rules
/// The URI [query] split into a map according to the rules
/// specified for FORM post in the [HTML 4.01 specification section
/// 17.13.4](https://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4
/// "HTML 4.01 section 17.13.4").
Expand All @@ -552,7 +611,7 @@ abstract interface class Uri {
/// The map is unmodifiable.
Map<String, String> get queryParameters;

/// Returns the URI query split into a map according to the rules
/// Returns the URI [query] split into a map according to the rules
/// specified for FORM post in the [HTML 4.01 specification section
/// 17.13.4](https://www.w3.org/TR/REC-html40/interact/forms.html#h-17.13.4
/// "HTML 4.01 section 17.13.4").
Expand All @@ -576,38 +635,38 @@ abstract interface class Uri {

/// Whether the URI is absolute.
///
/// A URI is an absolute URI in the sense of RFC 3986 if it has a scheme
/// and no fragment.
/// A URI is an absolute URI in the sense of RFC 3986 if it has a [scheme]
/// and no [fragment].
bool get isAbsolute;

/// Whether the URI has a [scheme] component.
bool get hasScheme => scheme.isNotEmpty;

/// Whether the URI has an [authority] component.
/// Whether the URI has an [authority] (hostname) component.
bool get hasAuthority;

/// Whether the URI has an explicit port.
/// Whether the URI has an explicit [port].
///
/// If the port number is the default port number
/// (zero for unrecognized schemes, with http (80) and https (443) being
/// recognized),
/// then the port is made implicit and omitted from the URI.
bool get hasPort;

/// Whether the URI has a query part.
/// Whether the URI has a [query] part.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do like the link, but it's technically a little misleading.
The Uri always has a [query] getter. It may or may not have a query component. (If not, the query getter returns the empty string.)

Keep it for now. It links to useful information.

bool get hasQuery;

/// Whether the URI has a fragment part.
/// Whether the URI has a [fragment] part.
bool get hasFragment;

/// Whether the URI has an empty path.
/// Whether the URI has an empty [path].
bool get hasEmptyPath;

/// Whether the URI has an absolute path (starting with '/').
/// Whether the URI has an absolute [path] (starting with '/').
bool get hasAbsolutePath;

/// Returns the origin of the URI in the form scheme://host:port for the
/// schemes http and https.
/// [scheme]s http and https.
///
/// It is an error if the scheme is not "http" or "https", or if the host name
/// is missing or empty.
Expand All @@ -633,7 +692,7 @@ abstract interface class Uri {
/// (one where [hasScheme] returns false).
bool isScheme(String scheme);

/// Creates a file path from a file URI.
/// Creates a file [path] from a file URI.
///
/// The returned path has either Windows or non-Windows
/// semantics.
Expand Down