diff --git a/src/commands/dev/mod.rs b/src/commands/dev/mod.rs index 3563edf27..5f51561c8 100644 --- a/src/commands/dev/mod.rs +++ b/src/commands/dev/mod.rs @@ -91,5 +91,8 @@ pub fn dev( ); } + if target.durable_objects.is_some() { + anyhow::bail!("Previewing a script that binds to a Durable Object Class is not supported using unauthenticated preview. Please use wrangler login or wrangler config.") + } gcs::dev(target, server_config, local_protocol, verbose) } diff --git a/src/commands/kv/mod.rs b/src/commands/kv/mod.rs index 96655bb45..b9b00e994 100644 --- a/src/commands/kv/mod.rs +++ b/src/commands/kv/mod.rs @@ -120,15 +120,17 @@ mod tests { binding: "KV".to_string(), }, ], + durable_objects: None, + migrations: None, name: "test-target".to_string(), target_type: TargetType::Webpack, webpack_config: None, site: None, vars: None, text_blobs: None, - usage_model: None, build: None, wasm_modules: None, + usage_model: None, }; assert!(kv::get_namespace_id(&target_with_dup_kv_bindings, "").is_err()); } diff --git a/src/lib.rs b/src/lib.rs index 74346e460..39f5ebfe1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ extern crate text_io; mod build; pub mod preview; +pub mod util; pub use build::build_target; pub mod commands; pub mod deploy; diff --git a/src/main.rs b/src/main.rs index fc0e8eb98..0671afd91 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,9 +19,11 @@ use wrangler::preview::{HttpMethod, PreviewOpt}; use wrangler::reporter; use wrangler::settings; use wrangler::settings::global_user::GlobalUser; +use wrangler::settings::toml::migrations::{MigrationConfig, Migrations}; use wrangler::settings::toml::TargetType; use wrangler::terminal::message::{Message, Output, StdOut}; use wrangler::terminal::{emoji, interactive, styles}; +use wrangler::util::ApplyToApp; use wrangler::version::background_check_for_updates; fn main() -> Result<()> { @@ -577,6 +579,7 @@ fn run() -> Result<()> { .takes_value(true) .possible_value("json") ) + .apply(MigrationConfig::add_to_app) , ) .subcommand( @@ -878,6 +881,11 @@ fn run() -> Result<()> { let manifest = settings::toml::Manifest::new(config_path)?; let env = matches.value_of("env"); let mut target = manifest.get_target(env, is_preview)?; + if let Some(adhoc) = MigrationConfig::from_matches(&matches) { + target.migrations = Some(Migrations { + migrations: vec![adhoc], + }); + } let deploy_config = manifest.get_deployments(env)?; if matches.is_present("output") && matches.value_of("output") == Some("json") { diff --git a/src/preview/mod.rs b/src/preview/mod.rs index 637c15b09..6581f521f 100644 --- a/src/preview/mod.rs +++ b/src/preview/mod.rs @@ -38,6 +38,10 @@ pub fn preview( } } + if target.durable_objects.is_some() { + anyhow::bail!("wrangler preview does not support previewing scripts that bind to durable object classes. Please use wrangler dev instead."); + } + build_target(&target)?; let sites_preview: bool = target.site.is_some(); diff --git a/src/reporter/mod.rs b/src/reporter/mod.rs index dd85a8bc7..38e3968f2 100644 --- a/src/reporter/mod.rs +++ b/src/reporter/mod.rs @@ -1,5 +1,8 @@ -use crate::commands::DEFAULT_CONFIG_PATH; use crate::settings::{self, toml::Manifest}; +use crate::{ + commands::DEFAULT_CONFIG_PATH, + settings::toml::{DurableObjects, UploadFormat}, +}; use std::env::args; use std::fs::{read_dir, File}; @@ -23,8 +26,10 @@ const BACKTRACE_PATH_PREFIX: &str = "backtrace::"; pub struct Report { uuid: Uuid, timestamp_ms: u128, - host_env: HashMap, // "os": "..." TODO: consider struct over HashMaps + host_env: HashMap, project_info: HashMap, + durable_objects: Option, + upload_format: Option, args: Vec, panic: Option, location: Option, @@ -53,11 +58,14 @@ pub fn read_log(log: Option<&str>) -> Result { /// gathers necessary error report information, and stores on disk until uploaded. the pub fn generate_report(panic_info: Option<&PanicInfo>) { + let project_info = load_project_info(); let mut report = Report { uuid: Uuid::new_v4(), timestamp_ms: 0, host_env: load_host_info(), - project_info: load_project_info(), + project_info: project_info.base, + durable_objects: project_info.durable_objects, + upload_format: project_info.upload_format, args: args().collect::>(), panic: panic_payload(panic_info), location: None, @@ -177,49 +185,65 @@ fn load_host_info() -> HashMap { host_info } +#[derive(Default)] +struct ProjectInfo { + base: HashMap, + durable_objects: Option, + upload_format: Option, +} + // wrangler project information -fn load_project_info() -> HashMap { - let mut project_info = HashMap::new(); +fn load_project_info() -> ProjectInfo { + let mut project_info: ProjectInfo = Default::default(); if let Ok(manifest) = Manifest::new(Path::new(DEFAULT_CONFIG_PATH)) { - project_info.insert("script_name".into(), manifest.name); - project_info.insert("account_id".into(), manifest.account_id); - project_info.insert( + project_info + .base + .insert("script_name".into(), manifest.name); + project_info + .base + .insert("account_id".into(), manifest.account_id); + project_info.base.insert( "zone_id".into(), manifest.zone_id.unwrap_or_else(|| "".into()), ); - project_info.insert("target_type".into(), manifest.target_type.to_string()); - project_info.insert( + project_info + .base + .insert("target_type".into(), manifest.target_type.to_string()); + project_info.base.insert( "workers_dev".into(), manifest.workers_dev.unwrap_or(false).to_string(), ); if let Some(builder) = manifest.build { - project_info.insert("using_custom_build".into(), "true".into()); + project_info + .base + .insert("using_custom_build".into(), "true".into()); if let Some((command, _)) = builder.build_command() { - project_info.insert("custom_build_command".into(), command.into()); + project_info + .base + .insert("custom_build_command".into(), command.into()); } - // TODO: encode the format's struct members in map field instead of only string literal - project_info.insert( - "upload_format".into(), - match builder.upload { - settings::toml::UploadFormat::ServiceWorker { .. } => "service-worker".into(), - settings::toml::UploadFormat::Modules { .. } => "modules".into(), - }, - ); + project_info.upload_format = Some(builder.upload); } if let Some(routes) = manifest.routes { - project_info.insert("routes".into(), routes.join(",")); + project_info.base.insert("routes".into(), routes.join(",")); } if let Some(route) = manifest.route { - project_info.insert("route".into(), route); + project_info.base.insert("route".into(), route); + } + + if let Some(usage_model) = &manifest.usage_model { + project_info + .base + .insert("usage_model".into(), usage_model.as_ref().to_string()); } - if let Some(usage_model) = manifest.usage_model { - project_info.insert("usage_model".into(), usage_model.as_ref().into()); + if let Some(durable_objects) = &manifest.durable_objects { + project_info.durable_objects = Some(durable_objects.clone()); } } diff --git a/src/settings/binding.rs b/src/settings/binding.rs index 8834003f7..d8015d1f5 100644 --- a/src/settings/binding.rs +++ b/src/settings/binding.rs @@ -4,10 +4,28 @@ use serde::Serialize; #[serde(tag = "type")] #[serde(rename_all = "snake_case")] pub enum Binding { - WasmModule { name: String, part: String }, - KvNamespace { name: String, namespace_id: String }, - TextBlob { name: String, part: String }, - PlainText { name: String, text: String }, + WasmModule { + name: String, + part: String, + }, + KvNamespace { + name: String, + namespace_id: String, + }, + #[serde(rename = "durable_object_namespace")] + DurableObjectsClass { + name: String, + class_name: String, + script_name: Option, + }, + TextBlob { + name: String, + part: String, + }, + PlainText { + name: String, + text: String, + }, } impl Binding { @@ -19,6 +37,18 @@ impl Binding { Binding::KvNamespace { name, namespace_id } } + pub fn new_durable_object_namespace( + name: String, + class_name: String, + script_name: Option, + ) -> Binding { + Binding::DurableObjectsClass { + name, + class_name, + script_name, + } + } + pub fn new_text_blob(name: String, part: String) -> Binding { Binding::TextBlob { name, part } } diff --git a/src/settings/toml/durable_objects.rs b/src/settings/toml/durable_objects.rs new file mode 100644 index 000000000..2a2aeda83 --- /dev/null +++ b/src/settings/toml/durable_objects.rs @@ -0,0 +1,27 @@ +use serde::{Deserialize, Serialize}; + +use crate::settings::binding::Binding; + +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] +pub struct DurableObjects { + #[serde(alias = "bindings")] + pub classes: Option>, +} + +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] +pub struct DurableObjectsClass { + #[serde(alias = "name")] + pub binding: String, + pub class_name: String, + pub script_name: Option, +} + +impl DurableObjectsClass { + pub fn binding(&self) -> Binding { + Binding::new_durable_object_namespace( + self.binding.clone(), + self.class_name.clone(), + self.script_name.clone(), + ) + } +} diff --git a/src/settings/toml/environment.rs b/src/settings/toml/environment.rs index 1f85a6f11..487190598 100644 --- a/src/settings/toml/environment.rs +++ b/src/settings/toml/environment.rs @@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize}; use serde_with::rust::string_empty_as_none; use crate::settings::toml::builder::Builder; +use crate::settings::toml::durable_objects::DurableObjects; use crate::settings::toml::kv_namespace::ConfigKvNamespace; use crate::settings::toml::route::RouteConfig; use crate::settings::toml::site::Site; @@ -30,6 +31,7 @@ pub struct Environment { pub vars: Option>, pub text_blobs: Option>, pub triggers: Option, + pub durable_objects: Option, } impl Environment { diff --git a/src/settings/toml/manifest.rs b/src/settings/toml/manifest.rs index 20ae62ad0..2c3d9e338 100644 --- a/src/settings/toml/manifest.rs +++ b/src/settings/toml/manifest.rs @@ -15,6 +15,7 @@ use crate::commands::{validate_worker_name, whoami, DEFAULT_CONFIG_PATH}; use crate::deploy::{self, DeployTarget, DeploymentSet}; use crate::settings::toml::builder::Builder; use crate::settings::toml::dev::Dev; +use crate::settings::toml::durable_objects::DurableObjects; use crate::settings::toml::environment::Environment; use crate::settings::toml::kv_namespace::{ConfigKvNamespace, KvNamespace}; use crate::settings::toml::route::RouteConfig; @@ -56,6 +57,7 @@ pub struct Manifest { pub text_blobs: Option>, pub wasm_modules: Option>, pub triggers: Option, + pub durable_objects: Option, #[serde(default, with = "string_empty_as_none")] pub usage_model: Option, } @@ -272,7 +274,12 @@ impl Manifest { deployments.push(DeployTarget::Schedule(scheduled)); } - if deployments.is_empty() { + let durable_objects = match env { + Some(e) => e.durable_objects.as_ref(), + None => self.durable_objects.as_ref(), + }; + + if durable_objects.is_none() && deployments.is_empty() { anyhow::bail!("No deployments specified!") } @@ -341,6 +348,8 @@ impl Manifest { // to include the name of the environment name: self.name.clone(), // Inherited kv_namespaces: get_namespaces(self.kv_namespaces.clone(), preview)?, // Not inherited + durable_objects: self.durable_objects.clone(), // Not inherited + migrations: None, // TODO(soon) Allow migrations in wrangler.toml site: self.site.clone(), // Inherited vars: self.vars.clone(), // Not inherited text_blobs: self.text_blobs.clone(), // Inherited @@ -365,6 +374,9 @@ impl Manifest { // don't inherit kv namespaces because it is an anti-pattern to use the same namespaces across multiple environments target.kv_namespaces = get_namespaces(environment.kv_namespaces.clone(), preview)?; + // don't inherit durable object configuration + target.durable_objects = environment.durable_objects.clone(); + // inherit site configuration if let Some(site) = &environment.site { target.site = Some(site.clone()); diff --git a/src/settings/toml/migrations.rs b/src/settings/toml/migrations.rs new file mode 100644 index 000000000..6e534c359 --- /dev/null +++ b/src/settings/toml/migrations.rs @@ -0,0 +1,262 @@ +use clap::{App, Arg, ArgGroup, ArgMatches}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] +pub struct Migrations { + pub migrations: Vec, +} + +impl Migrations { + pub fn api_migration(&self) -> Result { + // TODO: make api call to get most recent tag, and coalesce tags afterwards. + // For now, migrations will only ever have a single adhoc migration in it. + match &self.migrations { + migrations if migrations.len() == 1 => Ok(ApiMigration { + old_tag: None, + new_tag: None, + migration: migrations[0].migration.clone(), + }), + migrations => Self::coalesce_migrations(migrations), + } + } + + fn coalesce_migrations(_migrations: &[MigrationConfig]) -> Result { + unimplemented!() + } +} + +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] +pub struct MigrationConfig { + pub tag: Option, + #[serde(flatten)] + pub migration: Migration, +} + +impl MigrationConfig { + pub fn add_to_app<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> { + app + .arg( + Arg::with_name("new_class") + .help("allow durable objects to be created from a class in your script") + .long("new-class") + .takes_value(true) + .number_of_values(1) + .value_name("class name") + .multiple(true) + ) + .arg( + Arg::with_name("delete_class") + .help("delete all durable objects associated with a class in your script") + .long("delete-class") + .takes_value(true) + .number_of_values(1) + .value_name("class name") + .multiple(true) + ) + .arg( + Arg::with_name("rename_class") + .help("rename a durable object class") + .long("rename-class") + .takes_value(true) + .number_of_values(2) + .value_names(&["from class", "to class"]) + .multiple(true) + ) + .arg( + Arg::with_name("transfer_class") + .help("transfer all durable objects associated with a class in another script to a class in this script") + .long("transfer-class") + .takes_value(true) + .number_of_values(3) + .value_names(&["from script", "from class", "to class"]) + .multiple(true) + ) + .group( + ArgGroup::with_name("migrations") + .args(&["new_class", "delete_class", "rename_class", "transfer_class"]) + .multiple(true) + ) + } + + pub fn from_matches(matches: &ArgMatches) -> Option { + if matches.is_present("migrations") { + let new_classes = matches + .values_of("new_class") + .iter_mut() + .flatten() + .map(|c| c.to_owned()) + .collect(); + let deleted_classes = matches + .values_of("delete_class") + .iter_mut() + .flatten() + .map(|c| c.to_owned()) + .collect(); + let renamed_classes = matches + .values_of("rename_class") + .iter_mut() + .flatten() + .scan(None, |last_value: &mut Option<&str>, current_value| { + let rename = if let Some(last) = last_value { + let last = last.to_owned(); + *last_value = None; + Some(RenameClass { + from: last, + to: current_value.to_owned(), + }) + } else { + *last_value = Some(current_value); + None + }; + Some(rename) + }) + .filter_map(|m| m) + .collect(); + let transferred_classes = matches + .values_of("transfer_class") + .iter_mut() + .flatten() + .scan(Vec::<&str>::new(), |values, current_value| { + let mut transfer = None; + values.push(current_value); + if values.len() == 3 { + transfer = Some(TransferClass { + from_script: values[0].to_owned(), + from: values[1].to_owned(), + to: values[2].to_owned(), + }); + values.clear(); + } + Some(transfer) + }) + .filter_map(|m| m) + .collect(); + + let migration = Migration { + durable_objects: DurableObjectsMigration { + new_classes, + deleted_classes, + renamed_classes, + transferred_classes, + }, + }; + + log::info!("adhoc migration {:#?}", migration); + + Some(MigrationConfig { + tag: None, + migration, + }) + } else { + None + } + } +} + +#[derive(Clone, Debug, Default, PartialEq, Serialize)] +pub struct ApiMigration { + #[serde(skip_serializing_if = "Option::is_none")] + pub old_tag: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub new_tag: Option, + #[serde(flatten)] + pub migration: Migration, +} + +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] +pub struct Migration { + #[serde(flatten)] + pub durable_objects: DurableObjectsMigration, +} + +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] +pub struct DurableObjectsMigration { + pub new_classes: Vec, + pub deleted_classes: Vec, + pub renamed_classes: Vec, + pub transferred_classes: Vec, +} + +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] +pub struct RenameClass { + pub from: String, + pub to: String, +} + +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] +pub struct TransferClass { + pub from: String, + pub from_script: String, + pub to: String, +} + +#[cfg(test)] +mod tests { + use super::*; + + fn rename_class(tag: &str) -> RenameClass { + RenameClass { + from: format!("renameFrom{}", tag), + to: format!("renameTo{}", tag), + } + } + + fn transfer_class(tag: &str) -> TransferClass { + TransferClass { + from: format!("transferFromClass{}", tag), + from_script: format!("transferFromScript{}", tag), + to: format!("transferToClass{}", tag), + } + } + + #[test] + fn adhoc_migration_parsing() { + use crate::util::ApplyToApp; + let mut app = App::new("test").apply(MigrationConfig::add_to_app); + + let matches = app + .get_matches_from_safe_borrow(&[ + "test", + "--new-class", + "newA", + "--new-class", + "newB", + "--delete-class", + "deleteA", + "--delete-class", + "deleteB", + "--rename-class", + "renameFromA", + "renameToA", + "--rename-class", + "renameFromB", + "renameToB", + "--transfer-class", + "transferFromScriptA", + "transferFromClassA", + "transferToClassA", + "--transfer-class", + "transferFromScriptB", + "transferFromClassB", + "transferToClassB", + ]) + .unwrap(); + + let migration = MigrationConfig::from_matches(&matches); + + assert_eq!( + migration, + Some(MigrationConfig { + tag: None, + migration: Migration { + durable_objects: DurableObjectsMigration { + new_classes: vec![String::from("newA"), String::from("newB")], + deleted_classes: vec![String::from("deleteA"), String::from("deleteB")], + renamed_classes: vec![rename_class("A"), rename_class("B")], + transferred_classes: vec![transfer_class("A"), transfer_class("B")], + } + } + }) + ); + } +} diff --git a/src/settings/toml/mod.rs b/src/settings/toml/mod.rs index 11f9d7e15..7a831a04f 100644 --- a/src/settings/toml/mod.rs +++ b/src/settings/toml/mod.rs @@ -1,8 +1,10 @@ mod builder; mod dev; +mod durable_objects; mod environment; mod kv_namespace; mod manifest; +pub mod migrations; mod route; mod site; mod target; @@ -10,6 +12,7 @@ mod target_type; mod triggers; pub use builder::{Builder, ModuleRule, UploadFormat}; +pub use durable_objects::{DurableObjects, DurableObjectsClass}; pub use environment::Environment; pub use kv_namespace::{ConfigKvNamespace, KvNamespace}; pub use manifest::Manifest; diff --git a/src/settings/toml/target.rs b/src/settings/toml/target.rs index 8ce25e7cf..f3e1d3225 100644 --- a/src/settings/toml/target.rs +++ b/src/settings/toml/target.rs @@ -1,8 +1,9 @@ -use super::builder::Builder; +use super::durable_objects::DurableObjects; use super::kv_namespace::KvNamespace; use super::site::Site; use super::target_type::TargetType; use super::UsageModel; +use super::{builder::Builder, migrations::Migrations}; use std::collections::HashMap; use std::env; @@ -13,6 +14,8 @@ use std::path::PathBuf; pub struct Target { pub account_id: String, pub kv_namespaces: Vec, + pub durable_objects: Option, + pub migrations: Option, pub name: String, pub target_type: TargetType, pub webpack_config: Option, diff --git a/src/sites/mod.rs b/src/sites/mod.rs index 0d3b94559..678ff2131 100644 --- a/src/sites/mod.rs +++ b/src/sites/mod.rs @@ -310,6 +310,8 @@ mod tests { Target { account_id: "".to_string(), kv_namespaces: Vec::new(), + durable_objects: None, + migrations: None, name: "".to_string(), target_type: TargetType::JavaScript, webpack_config: None, diff --git a/src/upload/form/mod.rs b/src/upload/form/mod.rs index dff39ac1d..1b1127228 100644 --- a/src/upload/form/mod.rs +++ b/src/upload/form/mod.rs @@ -32,6 +32,11 @@ pub fn build( ) -> Result
{ let target_type = &target.target_type; let kv_namespaces = &target.kv_namespaces; + let durable_object_classes = target + .durable_objects + .as_ref() + .and_then(|d| d.classes.clone()) + .unwrap_or_default(); let mut text_blobs: Vec = Vec::new(); let mut plain_texts: Vec = Vec::new(); let mut wasm_modules: Vec<WasmModule> = Vec::new(); @@ -81,6 +86,7 @@ pub fn build( script_path, wasm_modules, kv_namespaces.to_vec(), + durable_object_classes, text_blobs, plain_texts, )?; @@ -99,6 +105,7 @@ pub fn build( script_path, wasm_modules, kv_namespaces.to_vec(), + durable_object_classes, text_blobs, plain_texts, )?; @@ -106,10 +113,17 @@ pub fn build( service_worker::build_form(&assets, session_config) } UploadFormat::Modules { main, dir, rules } => { + let migration = match &target.migrations { + Some(migrations) => Some(migrations.api_migration()?), + None => None, + }; + let module_config = ModuleConfig::new(main, dir, rules); let assets = ModulesAssets::new( module_config.get_modules()?, kv_namespaces.to_vec(), + durable_object_classes, + migration, plain_texts, )?; @@ -126,6 +140,7 @@ pub fn build( script_path, wasm_modules, kv_namespaces.to_vec(), + durable_object_classes, text_blobs, plain_texts, )?; @@ -152,6 +167,7 @@ pub fn build( script_path, wasm_modules, kv_namespaces.to_vec(), + durable_object_classes, text_blobs, plain_texts, )?; diff --git a/src/upload/form/modules_worker.rs b/src/upload/form/modules_worker.rs index 2ff902836..a79f7d911 100644 --- a/src/upload/form/modules_worker.rs +++ b/src/upload/form/modules_worker.rs @@ -5,6 +5,7 @@ use reqwest::blocking::multipart::{Form, Part}; use serde::Serialize; use crate::settings::binding::Binding; +use crate::settings::toml::migrations::ApiMigration; use super::ModulesAssets; @@ -12,6 +13,7 @@ use super::ModulesAssets; struct Metadata { pub main_module: String, pub bindings: Vec<Binding>, + pub migrations: Option<ApiMigration>, } pub fn build_form( @@ -48,6 +50,7 @@ fn add_metadata(mut form: Form, assets: &ModulesAssets) -> Result<Form> { let metadata_json = serde_json::json!(&Metadata { main_module: assets.manifest.main.clone(), bindings: assets.bindings(), + migrations: assets.migration.clone() }); let metadata = Part::text(metadata_json.to_string()) diff --git a/src/upload/form/project_assets.rs b/src/upload/form/project_assets.rs index 4604bd2c6..9469ccf8a 100644 --- a/src/upload/form/project_assets.rs +++ b/src/upload/form/project_assets.rs @@ -12,7 +12,9 @@ use super::plain_text::PlainText; use super::text_blob::TextBlob; use super::wasm_module::WasmModule; -use crate::settings::toml::{KvNamespace, ModuleRule}; +use crate::settings::toml::{ + migrations::ApiMigration, DurableObjectsClass, KvNamespace, ModuleRule, +}; use std::collections::{HashMap, HashSet}; #[derive(Debug)] @@ -21,6 +23,7 @@ pub struct ServiceWorkerAssets { script_path: PathBuf, pub wasm_modules: Vec<WasmModule>, pub kv_namespaces: Vec<KvNamespace>, + pub durable_object_classes: Vec<DurableObjectsClass>, pub text_blobs: Vec<TextBlob>, pub plain_texts: Vec<PlainText>, } @@ -30,6 +33,7 @@ impl ServiceWorkerAssets { script_path: PathBuf, wasm_modules: Vec<WasmModule>, kv_namespaces: Vec<KvNamespace>, + durable_object_classes: Vec<DurableObjectsClass>, text_blobs: Vec<TextBlob>, plain_texts: Vec<PlainText>, ) -> Result<Self> { @@ -41,6 +45,7 @@ impl ServiceWorkerAssets { script_path, wasm_modules, kv_namespaces, + durable_object_classes, text_blobs, plain_texts, }) @@ -57,6 +62,10 @@ impl ServiceWorkerAssets { let binding = kv.binding(); bindings.push(binding); } + for do_ns in &self.durable_object_classes { + let binding = do_ns.binding(); + bindings.push(binding); + } for blob in &self.text_blobs { let binding = blob.binding(); bindings.push(binding); @@ -319,6 +328,8 @@ fn build_type_matchers(rules: Vec<ModuleRule>) -> Result<Vec<ModuleMatcher>> { pub struct ModulesAssets { pub manifest: ModuleManifest, pub kv_namespaces: Vec<KvNamespace>, + pub durable_object_classes: Vec<DurableObjectsClass>, + pub migration: Option<ApiMigration>, pub plain_texts: Vec<PlainText>, } @@ -326,11 +337,15 @@ impl ModulesAssets { pub fn new( manifest: ModuleManifest, kv_namespaces: Vec<KvNamespace>, + durable_object_classes: Vec<DurableObjectsClass>, + migration: Option<ApiMigration>, plain_texts: Vec<PlainText>, ) -> Result<Self> { Ok(Self { manifest, kv_namespaces, + durable_object_classes, + migration, plain_texts, }) } @@ -345,6 +360,10 @@ impl ModulesAssets { let binding = kv.binding(); bindings.push(binding); } + for class in &self.durable_object_classes { + let binding = class.binding(); + bindings.push(binding); + } for plain_text in &self.plain_texts { let binding = plain_text.binding(); bindings.push(binding); diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 000000000..84b9200b5 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,10 @@ +use clap::App; +pub trait ApplyToApp<'a, 'b> { + fn apply(self, f: fn(App<'a, 'b>) -> App<'a, 'b>) -> App<'a, 'b>; +} + +impl<'a, 'b> ApplyToApp<'a, 'b> for App<'a, 'b> { + fn apply(self, f: fn(App<'a, 'b>) -> App<'a, 'b>) -> App<'a, 'b> { + f(self) + } +}