///|
fn is_unreserved_byte(byte : Byte) -> Bool {
let n = byte.to_int()
(n >= 65 && n <= 90) ||
(n >= 97 && n <= 122) ||
(n >= 48 && n <= 57) ||
byte is b'-' ||
byte is b'_' ||
byte is b'.' ||
byte is b'~'
}
///|
fn hex_digit(n : Int) -> Char {
if n < 10 {
(n + '0'.to_int()).unsafe_to_char()
} else {
(n - 10 + 'A'.to_int()).unsafe_to_char()
}
}
///|
fn percent_encode(input : StringView, slash_okay? : Bool = false) -> String {
let out = StringBuilder()
for ch in input {
if ch.is_ascii() {
let byte = ch.to_int().to_byte()
if is_unreserved_byte(byte) || (slash_okay && byte is b'/') {
out.write_char(ch)
} else {
let n = byte.to_int()
out.write_char('%')
out.write_char(hex_digit(n / 16))
out.write_char(hex_digit(n % 16))
}
} else {
let bytes = @utf8.encode(ch.to_string())
for byte in bytes {
let n = byte.to_int()
out.write_char('%')
out.write_char(hex_digit(n / 16))
out.write_char(hex_digit(n % 16))
}
}
}
out.to_string()
}
///|
fn endpoint_origin_and_host(
config : Config,
bucket : String,
) -> (String, String) {
let endpoint = match config.endpoint {
Some(value) => value.trim_end(chars="/").to_owned()
None => "https://s3.\{config.region}.amazonaws.com"
}
match config.endpoint_style {
Path => {
let (scheme, endpoint_host) = endpoint_parts(endpoint)
("\{scheme}://\{endpoint_host}", endpoint_host)
}
VirtualHost => {
let (scheme, endpoint_host) = endpoint_parts(endpoint)
let origin_host = "\{bucket}.\{endpoint_host}"
("\{scheme}://\{origin_host}", origin_host)
}
}
}
///|
fn endpoint_parts(endpoint : String) -> (String, String) {
match endpoint.find("://") {
Some(i) => (endpoint[:i].to_owned(), endpoint[i + 3:].to_owned())
None => ("https", endpoint)
}
}
///|
fn canonical_object_path(
config : Config,
bucket : String,
key : String,
) -> String {
let object = if key == "" { "/" } else { "/\{key}" }
match config.endpoint_style {
VirtualHost => percent_encode(object, slash_okay=true)
Path =>
"/\{percent_encode(bucket)}\{percent_encode(object, slash_okay=true)}"
}
}
///|
fn canonical_query(query : Map[String, String]) -> String {
let pairs = query.to_array()
pairs.sort_by(fn(left, right) {
let lk = percent_encode(left.0)
let rk = percent_encode(right.0)
let c = lk.compare(rk)
if c == 0 {
percent_encode(left.1).compare(percent_encode(right.1))
} else {
c
}
})
pairs
.map(fn(pair) { "\{percent_encode(pair.0)}=\{percent_encode(pair.1)}" })
.join("&")
}
///|
fn append_query(path : String, query : Map[String, String]) -> String {
if query.is_empty() {
path
} else {
"\{path}?\{canonical_query(query)}"
}
}