//
// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/url
//

= Segments

Hierarchical schemes often interpret the path as a slash-delimited sequence of percent-encoded strings called segments.
In this library the segments may be accessed using these separate, bidirectional view types which reference the underlying URL:

[cols="1,1,3"]
|===
// Headers
|Type|Accessor|Description

// Row 1, Column 1
|cpp:segments_view[]
// Row 1, Column 2
|cpp:url_view_base::segments[segments]
// Row 1, Column 3
|A read-only range of decoded segments.

// Row 2, Column 1
|cpp:segments_ref[]
// Row 2, Column 2
|cpp:url_view_base::segments[segments]
// Row 2, Column 3
|A modifiable range of decoded segments.

// Row 3, Column 1
|cpp:segments_encoded_view[]
// Row 3, Column 2
|cpp:url_view_base::encoded_segments[encoded_segments]
// Row 3, Column 3
|A read-only range of segments.

// Row 4, Column 1
|cpp:segments_encoded_ref[]
// Row 4, Column 2
|cpp:url_view_base::encoded_segments[encoded_segments]
// Row 4, Column 3
|A modifiable range of segments.

|===

First we observe these invariants about paths and segments:

* All URLs have a path
* A path is absolute, relative, or empty
* Paths starting with "/" are absolute
* A relative path can never follow an authority
* Every path maps to a unique range of segments, plus a cpp:bool[] indicating if the path is absolute.
* Every range of segments, plus a cpp:bool[] indicating if the path is absolute, maps to a unique path.

The following URL contains a path with three segments: "path", "to", and "file.txt":

[source]
----
http://www.example.com/path/to/file.txt
----

To understand the relationship between the path and segments, we define this function cpp:segs[] which returns a list of strings corresponding to the elements in a container of segments:


[source,cpp]
----
include::example$unit/doc_3_urls.cpp[tag=code_container_4_1,indent=0]
----

In this table we show the result of invoking cpp:segs[] with different paths.
This demonstrates how the library achieves the invariants described above for various interesting cases:

[cols="3,3,1"]
|===
// Headers
|cpp:s[]|cpp:segs( s )[]|absolute

// Row 1, Column 1
|cpp:""[]
// Row 1, Column 2
|cpp:{ }[]
// Row 1, Column 3
|

// Row 2, Column 1
|cpp:"/"[]
// Row 2, Column 2
|cpp:{ }[]
// Row 2, Column 3
|yes

// Row 3, Column 1
|cpp:"./"[]
// Row 3, Column 2
|cpp:{ "" }[]
// Row 3, Column 3
|

// Row 4, Column 1
|cpp:"usr"[]
// Row 4, Column 2
|cpp:{ "usr" }[]
// Row 4, Column 3
|

// Row 5, Column 1
|cpp:"./usr"[]
// Row 5, Column 2
|cpp:{ "usr" }[]
// Row 5, Column 3
|

// Row 6, Column 1
|cpp:"/index.htm"[]
// Row 6, Column 2
|cpp:{ "index.htm" }[]
// Row 6, Column 3
|yes

// Row 7, Column 1
|cpp:"/images/cat-pic.gif"[]
// Row 7, Column 2
|cpp:{ "images", "cat-pic.gif" }[]
// Row 7, Column 3
|yes

// Row 8, Column 1
|cpp:"images/cat-pic.gif"[]
// Row 8, Column 2
|cpp:{ "images", "cat-pic.gif" }[]
// Row 8, Column 3
|

// Row 9, Column 1
|cpp:"/fast//query"[]
// Row 9, Column 2
|cpp:{ "fast", "", "query" }[]
// Row 9, Column 3
|yes

// Row 10, Column 1
|cpp:"fast//"[]
// Row 10, Column 2
|cpp:{ "fast", "", "" }[]
// Row 10, Column 3
|

// Row 11, Column 1
|cpp:"/./"[]
// Row 11, Column 2
|cpp:{ "" }[]
// Row 11, Column 3
|yes

// Row 12, Column 1
|cpp:".//"[]
// Row 12, Column 2
|cpp:{ "", "" }[]
// Row 12, Column 3
|

|===

This implies that two paths may map to the same sequence of segments . In the paths cpp:"usr"[] and cpp:"./usr"[], the cpp:"./"[] is a prefix that might be necessary to maintain the invariant that instances of cpp:url_view_base[] always refer to valid URLs.
Thus, both paths map to cpp:{ "usr" }[].
On the other hand, each sequence determines a unique path for a given URL.
For instance, setting the segments to cpp:{"a"}[] would always map to either cpp:"./a"[] or cpp:"a"[], depending on whether the "." prefix is necessary to keep the URL valid.

Sequences don't iterate the leading "." when it's necessary to keep the URL valid.
Thus, when we assign cpp:{ "x", "y", "z" }[] to segments, the sequence always contains cpp:{ "x", "y", "z" }[] after that.
It never contains cpp:{ ".", "x", "y", "z" }[] because the cpp:"."[] needed to be included.
In other words, the contents of the segment container are authoritative, and the path string is a function of them.
Not vice-versa.

Library algorithms which modify individual segments of the path or set the entire path attempt to behave consistently with the behavior expected as if the operation was performed on the equivalent sequence.
If a path maps, say, to the three element sequence cpp:{ "a", "b", "c" }[] then erasing the middle segment should result in the sequence cpp:{ "a", "c" }[].
The library always strives to do exactly what the caller requests; however, in some cases this would result in either an invalid URL, or a dramatic and unwanted change in the URL's semantics.

For example consider the following URL:

[source,cpp]
----
url u = url().set_path( "kyle:xy" );
----

The library produces the URL string cpp:"kyle%3Axy"[] and not cpp:"kyle:xy"[], because the latter would have an unintended scheme.
The table below demonstrates the results achieved by performing various modifications to a URL containing a path:

//     Functions which may need
//     to alter the path prefix:
// 
//     * convert ':' to %3A
//         remove_scheme()
// 
//     * add "/."
//         remove_authority()
// 
//     * convert ':' to %3A or add "/."
//         remove_origin()
// 
//     * convert %3A to ':'
//         set_scheme_impl()
//             set_scheme()
//             set_scheme_id()
// 
//     * convert ':' to %3A
//     * convert %3A to ':'
//         set_path_absolute()
// 
//     * convert ':' to %3A or add "/."
//         set_path()
// 
//     * don't canonicalize %3A in segment-nc-nz
//         normalize()
// 
//     ????
//         set_encoded_path()
//         edit_segments()

[cols="1,1,1"]
|===
// Headers
|URL|Operation|Result

// Row 1, Column 1
|cpp:"info:kyle:xy"[]
// Row 1, Column 2
|cpp:remove_scheme()[]
// Row 1, Column 3
|cpp:"kyle%3Axy"[]

// Row 2, Column 1
|cpp:"kyle%3Axy"[]
// Row 2, Column 2
|cpp:set_scheme( "gopher" )[]
// Row 2, Column 3
|cpp:"gopher:kyle:xy"[]

// Row 3, Column 1
|cpp:"http://www.example.com//kyle:xy"[]
// Row 3, Column 2
|cpp:remove_authority()[]
// Row 3, Column 3
|cpp:"http:/.//kyle:xy"[]

// Row 4, Column 1
|cpp:"//www.example.com//kyle:xy"[]
// Row 4, Column 2
|cpp:remove_authority()[]
// Row 4, Column 3
|cpp:"/.//kyle:xy"[]

// Row 5, Column 1
|cpp:"http://www.example.com//kyle:xy"[]
// Row 5, Column 2
|cpp:remove_origin()[]
// Row 5, Column 3
|cpp:"/.//kyle:xy"[]

// Row 6, Column 1
|cpp:"info:kyle:xy"[]
// Row 6, Column 2
|cpp:remove_origin()[]
// Row 6, Column 3
|cpp:"kyle%3Axy"[]

// Row 7, Column 1
|cpp:"/kyle:xy"[]
// Row 7, Column 2
|cpp:set_path_absolute( false )[]
// Row 7, Column 3
|cpp:"kyle%3Axy"[]

// Row 8, Column 1
|cpp:"kyle%3Axy"[]
// Row 8, Column 2
|cpp:set_path_absolute( true )[]
// Row 8, Column 3
|cpp:"/kyle:xy"[]

// Row 9, Column 1
|cpp:""[]
// Row 9, Column 2
|cpp:set_path( "kyle:xy" )[]
// Row 9, Column 3
|cpp:"kyle%3Axy"[]

// Row 10, Column 1
|cpp:""[]
// Row 10, Column 2
|cpp:set_path( "//foo/fighters.txt" )[]
// Row 10, Column 3
|cpp:"/.//foo/fighters.txt"[]

// Row 11, Column 1
|cpp:"my%3Asharona/billa%3Abong"[]
// Row 11, Column 2
|cpp:normalize()[]
// Row 11, Column 3
|cpp:"my%3Asharona/billa:bong"[]

// Row 12, Column 1
|cpp:"./my:sharona"[]
// Row 12, Column 2
|cpp:normalize()[]
// Row 12, Column 3
|cpp:"my%3Asharona"[]

|===

For the full set of containers and functions for operating on paths and segments, please consult the reference.


