use std::collections::HashSet;
use cloudflare::framework::response::ApiFailure;
use anyhow::Result;
use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
use crate::http;
use crate::settings::toml::Target;
pub mod bulk;
pub mod key;
pub mod namespace;
pub fn format_error(e: ApiFailure) -> String {
http::format_error(e, Some(&kv_help))
}
fn kv_help(error_code: u16) -> &'static str {
match error_code {
7003 | 7000 => {
"Your configuration file is likely missing the field \"account_id\", which is required to write to Workers KV."
}
10010 | 10011 | 10012 | 10013 | 10014 | 10018 => {
"Run `wrangler kv:namespace list` to see your existing namespaces with IDs"
}
10009 => "Run `wrangler kv:key list` to see your existing keys", 10022 | 10024 | 10030 => "See documentation",
10021 | 10035 | 10038 => "Consider moving this namespace",
_ => "",
}
}
fn check_duplicate_namespaces(target: &Target) -> bool {
let mut binding_names: HashSet<String> = HashSet::new();
for namespace in &target.kv_namespaces {
if binding_names.contains(&namespace.binding) {
return true;
} else {
binding_names.insert(namespace.binding.clone());
}
}
false
}
pub fn get_namespace_id(target: &Target, binding: &str) -> Result<String> {
if check_duplicate_namespaces(target) {
anyhow::bail!(
"Namespace binding \"{}\" is duplicated in \"{}\"",
binding,
target.name
)
}
for namespace in &target.kv_namespaces {
if namespace.binding == binding {
return Ok(namespace.id.to_string());
}
}
anyhow::bail!(
"Namespace binding \"{}\" not found in \"{}\"",
binding,
target.name
)
}
const KV_ASCII_SET: &AsciiSet = &CONTROLS.add(b'/');
fn url_encode_key(key: &str) -> String {
utf8_percent_encode(key, KV_ASCII_SET).to_string()
}
#[cfg(test)]
mod tests {
use crate::commands::kv;
use crate::settings::toml::{KvNamespace, Target, TargetType};
#[test]
fn it_can_detect_duplicate_bindings() {
let target_with_dup_kv_bindings = Target {
account_id: None.into(),
kv_namespaces: vec![
KvNamespace {
id: "fake".to_string(),
binding: "KV".to_string(),
},
KvNamespace {
id: "fake".to_string(),
binding: "KV".to_string(),
},
],
r2_buckets: Vec::new(),
durable_objects: None,
migrations: None,
name: "test-target".to_string(),
target_type: TargetType::Webpack,
webpack_config: None,
site: None,
vars: None,
text_blobs: None,
build: None,
wasm_modules: None,
usage_model: None,
compatibility_date: None,
compatibility_flags: Vec::new(),
};
assert!(kv::get_namespace_id(&target_with_dup_kv_bindings, "").is_err());
}
#[test]
fn it_encodes_slash() {
assert_eq!(kv::url_encode_key("/slash"), "%2Fslash");
}
#[test]
fn it_doesnt_double_encode_slash() {
assert_eq!(kv::url_encode_key("%2Fslash"), "%2Fslash");
}
}