use base64::{Engine, engine::general_purpose::URL_SAFE};
use iri_string::types::{UriAbsoluteStr, UriAbsoluteString};
use crate::vscode_sys;
pub fn from_vscode(vscode_uri: &vscode_sys::Uri) -> Result<UriAbsoluteString, napi::Error> {
let uri_string = vscode_uri.to_string_skip_encoding(true)?;
UriAbsoluteString::try_from(uri_string)
.map_err(|error| napi::Error::from_reason(format!("Failed to parse URI: {error}")))
}
pub fn to_vscode<'env>(
env: &'env napi::Env,
uri: &UriAbsoluteStr,
) -> Result<vscode_sys::Uri<'env>, napi::Error> {
vscode_sys::Uri::parse(env, uri.as_str(), true)
}
pub fn encode(vscode_uri: &vscode_sys::Uri) -> Result<UriAbsoluteString, napi::Error> {
let uri_to_encode = vscode_uri.to_string_skip_encoding(true)?;
let encoded_path = format!("/{}", URL_SAFE.encode(uri_to_encode));
let mut builder = iri_string::build::Builder::new();
builder.scheme(crate::PIJUL_SCHEME);
// TODO: support different hosts with different actions
builder.host("tracked");
builder.path(&encoded_path);
builder.normalize();
let built_uri = builder
.build()
.map_err(|error| napi::Error::from_reason(format!("Failed to build URI: {error}")))?;
Ok(UriAbsoluteString::from(built_uri))
}
pub fn decode(vscode_uri: &vscode_sys::Uri) -> Result<UriAbsoluteString, napi::Error> {
// Example: pijul:tracked/dnNjb2RlOmV4YW1wbGUvdXJp
let pijul_uri = from_vscode(vscode_uri)?;
// Example: dnNjb2RlOmV4YW1wbGUvdXJp
let uri_to_decode = pijul_uri.path_str().strip_prefix('/').ok_or_else(|| {
napi::Error::from_reason(format!("URI path did not contain `/`: {pijul_uri}"))
})?;
let decoded_bytes = URL_SAFE.decode(uri_to_decode).map_err(|error| {
napi::Error::from_reason(format!("Failed to decode base64 bytes: {error}"))
})?;
// Example: vscode:example/uri
let decoded_text = String::from_utf8(decoded_bytes).map_err(|error| {
napi::Error::from_reason(format!("Failed to parse UTF-8 bytes: {error}"))
})?;
UriAbsoluteString::try_from(decoded_text)
.map_err(|error| napi::Error::from_reason(format!("Failed to parse decoded URI: {error}")))
}