├── .gitignore ├── guest ├── .gitignore ├── Cargo.toml ├── spin.toml ├── src │ └── lib.rs └── Cargo.lock ├── CONTRIBUTING.md ├── CODE_OF_CONDUCT.md ├── spin-pluginify.toml ├── MAINTAINERS.md ├── src ├── utils.rs ├── main.rs ├── aws.rs └── lib.rs ├── sqs.wit ├── Cargo.toml ├── release-process.md ├── README.md ├── .github └── workflows │ └── build.yml └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.tar.gz 3 | trigger-sqs.json 4 | -------------------------------------------------------------------------------- /guest/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.tar.gz 3 | trigger-sqs.json 4 | /.spin -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This project subscribes to the Spin [Code of Conduct](https://github.com/spinframework/spin/blob/main/CODE_OF_CONDUCT.md). 4 | -------------------------------------------------------------------------------- /spin-pluginify.toml: -------------------------------------------------------------------------------- 1 | name = "trigger-sqs" 2 | description = "A Spin trigger for Amazon SQS events" 3 | version = "0.11.0" 4 | spin_compatibility = ">=2.2" 5 | license = "Apache-2.0" 6 | package = "./target/release/trigger-sqs" 7 | -------------------------------------------------------------------------------- /guest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "guest" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = [ "cdylib" ] 8 | 9 | [dependencies] 10 | anyhow = "1" 11 | bytes = "1" 12 | http = "0.2" 13 | spin-sdk = "5.0" 14 | wit-bindgen = "0.43.0" 15 | 16 | [workspace] 17 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # MAINTAINERS 2 | 3 | ## Current Maintainers 4 | 5 | _Listed in alphabetical order by first name_ 6 | 7 | | Name | GitHub Username | 8 | | --- | --- | 9 | | Caleb Schoepp | calebschoepp | 10 | | Ivan Towlson | itowlson | 11 | | Kate Goldenring | kate-goldenring | 12 | | Vaughn Dice | vdice | 13 | 14 | ## Emeritus Maintainers 15 | 16 | None -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | const UNKNOWN_ID: &str = "[unknown id]"; 2 | 3 | pub trait MessageUtils { 4 | fn display_id(&self) -> String; 5 | } 6 | 7 | impl MessageUtils for aws_sdk_sqs::types::Message { 8 | fn display_id(&self) -> String { 9 | self.message_id().unwrap_or(UNKNOWN_ID).to_owned() 10 | } 11 | } 12 | 13 | impl MessageUtils for crate::sqs::Message { 14 | fn display_id(&self) -> String { 15 | self.id.as_deref().unwrap_or(UNKNOWN_ID).to_owned() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /guest/spin.toml: -------------------------------------------------------------------------------- 1 | spin_manifest_version = 2 2 | 3 | [application] 4 | name = "sqs-sample" 5 | authors = ["itowlson "] 6 | description = "" 7 | version = "0.1.0" 8 | 9 | [[trigger.sqs]] 10 | component = "itowlsontest" 11 | queue_url = "https://sqs.us-west-2.amazonaws.com/177456779558/itowlsontest" 12 | max_messages = 1 13 | idle_wait_seconds = 3 14 | system_attributes = ["All"] 15 | message_attributes = ["glonk"] 16 | 17 | [component.itowlsontest] 18 | source = "target/wasm32-wasip2/release/guest.wasm" 19 | [component.itowlsontest.build] 20 | command = "cargo build --target wasm32-wasip2 --release" 21 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use spin_runtime_factors::FactorsBuilder; 3 | use spin_trigger::cli::FactorsTriggerCommand; 4 | use trigger_sqs::SqsTrigger; 5 | 6 | type Command = FactorsTriggerCommand; 7 | 8 | #[tokio::main] 9 | async fn main() -> anyhow::Result<()> { 10 | spin_telemetry::init(build_info())?; 11 | 12 | let t = Command::parse(); 13 | t.run().await 14 | } 15 | 16 | /// Returns build information of the parent Spin process, similar to: 0.1.0 (2be4034 2022-03-31). 17 | fn build_info() -> String { 18 | let spin_version = env_var("SPIN_VERSION"); 19 | let spin_commit_sha = env_var("SPIN_COMMIT_SHA"); 20 | let spin_commit_date = env_var("SPIN_COMMIT_DATE"); 21 | format!("{spin_version} ({spin_commit_sha} {spin_commit_date})") 22 | } 23 | 24 | fn env_var(name: &str) -> String { 25 | std::env::var(name).unwrap_or_else(|_| "unknown".to_string()) 26 | } 27 | -------------------------------------------------------------------------------- /sqs.wit: -------------------------------------------------------------------------------- 1 | package fermyon:spin-sqs@2.0.0; 2 | 3 | interface sqs-types { 4 | 5 | variant message-attribute-value { 6 | str(string), // TODO: parse for the number case? 7 | binary(list), 8 | } 9 | 10 | record message-attribute { 11 | name: string, 12 | data-type: option, 13 | value: message-attribute-value, 14 | } 15 | 16 | record message { 17 | id: option, 18 | // TODO: built-in attributes? E.g. timestamps 19 | message-attributes: list, 20 | body: option, 21 | } 22 | 23 | variant error { 24 | other(string), 25 | } 26 | 27 | enum message-action { 28 | delete, 29 | leave, 30 | } 31 | } 32 | 33 | world spin-sqs { 34 | use sqs-types.{message, message-action, error}; 35 | export handle-queue-message: func(message: message) -> result; 36 | } 37 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "trigger-sqs" 3 | version = "0.11.0" 4 | edition = "2021" 5 | rust-version = "1.86" 6 | 7 | [dependencies] 8 | anyhow = "1.0.68" 9 | aws-config = "1.5.0" 10 | aws-sdk-sqs = "1.5.0" 11 | clap = { version = "3.1.15", features = ["derive", "env"] } 12 | futures = "0.3.31" 13 | serde = "1.0" 14 | spin-core = { git = "https://github.com/spinframework/spin", tag = "v3.5.0" } 15 | spin-factors = { git = "https://github.com/spinframework/spin", tag = "v3.5.0" } 16 | spin-runtime-factors = { git = "https://github.com/spinframework/spin", tag = "v3.5.0" } 17 | spin-telemetry = { git = "https://github.com/spinframework/spin", tag = "v3.5.0" } 18 | spin-trigger = { git = "https://github.com/spinframework/spin", tag = "v3.5.0" } 19 | tokio = { version = "1", features = ["rt", "macros", "time", "signal"] } 20 | tokio-scoped = "0.2.0" 21 | tracing = { version = "0.1", features = ["log"] } 22 | wasmtime = { version = "37.0.1" } 23 | 24 | [target.'cfg(target_os = "linux")'.dependencies] 25 | # This needs to be an explicit dependency to enable 26 | # '--features openssl/vendored', which is used for Linux releases. 27 | openssl = { version = "0.10" } 28 | -------------------------------------------------------------------------------- /guest/src/lib.rs: -------------------------------------------------------------------------------- 1 | wit_bindgen::generate!({ 2 | world: "spin-sqs", 3 | path: "..", 4 | }); 5 | 6 | use fermyon::spin_sqs::sqs_types as sqs; 7 | 8 | struct Sqs; 9 | export!(Sqs); 10 | 11 | impl Guest for Sqs { 12 | fn handle_queue_message(message: sqs::Message,) -> Result { 13 | println!("I GOT A MESSAGE! ID: {:?}", message.id); 14 | for attr in message.message_attributes { 15 | println!(" ... ATTR {}: {:?}", attr.name, attr.value); 16 | } 17 | println!(" ... BODY: {:?}", message.body); 18 | 19 | if let Some(b) = message.body.as_ref() { 20 | if b.contains("ERROR") { 21 | return Err(sqs::Error::Other("YOU triggered this error Towlson YOU did this".to_owned())); 22 | } 23 | } 24 | 25 | if let Some(mid) = message.id { 26 | if let Some(last_char) = mid.chars().last() { 27 | if let Some(num) = last_char.to_digit(10) { 28 | for i in 0..(num + 4) { 29 | println!(" ... thinking for {i} secs"); 30 | std::thread::sleep(std::time::Duration::from_secs(i.into())); 31 | } 32 | } 33 | } 34 | } 35 | 36 | Ok(sqs::MessageAction::Delete) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /release-process.md: -------------------------------------------------------------------------------- 1 | # Cutting a new release of the Spin SQS trigger plugin 2 | 3 | To cut a new release of the SQS trigger plugin, you will need to do the following: 4 | 5 | 1. Confirm that [CI is green](https://github.com/spinframework/spin-trigger-sqs/actions) for the commit selected to be tagged and released. 6 | 7 | 1. Change the version number in [Cargo.toml](./Cargo.toml) and [spin-pluginify.toml](./spin-pluginify.toml) and run `cargo build --release`. 8 | 9 | 1. Create a pull request with these changes and merge once approved. 10 | 11 | 1. Checkout the commit with the version bump from above. 12 | 13 | 1. Create and push a new tag with a `v` and then the version number. 14 | 15 | As an example, via the `git` CLI: 16 | 17 | ``` 18 | # Create a GPG-signed and annotated tag 19 | git tag -s -m "Spin SQS Trigger v0.6.0" v0.6.0 20 | 21 | # Push the tag to the remote corresponding to spinframework/spin-trigger-sqs (here 'origin') 22 | git push origin v0.6.0 23 | ``` 24 | 25 | 1. Pushing the tag upstream will trigger the [release action](https://github.com/spinframework/spin-trigger-sqs/actions/workflows/release.yml). 26 | - The release build will create the packaged versions of the plugin, the updated plugin manifest and a checksums file 27 | - These assets are uploaded to a new GitHub release for the pushed tag 28 | - Release notes are auto-generated but edit as needed especially around breaking changes or other notable items 29 | 30 | 1. Create a PR in the [spinframework/spin-plugins](https://github.com/spinframework/spin-plugins) repository with the [updated manifest](https://github.com/spinframework/spin-plugins/tree/main/manifests/sqs-trigger). 31 | 32 | 1. If applicable, create PR(s) or coordinate [documentation](https://github.com/spinframework/spin-docs) needs, e.g. for new features or updated functionality. -------------------------------------------------------------------------------- /src/aws.rs: -------------------------------------------------------------------------------- 1 | pub use aws_sdk_sqs::types::Message; 2 | pub use aws_sdk_sqs::types::MessageAttributeValue; 3 | pub use aws_sdk_sqs::types::MessageSystemAttributeName; 4 | pub use aws_sdk_sqs::types::QueueAttributeName; 5 | pub use aws_sdk_sqs::Client; 6 | 7 | use crate::utils::MessageUtils; 8 | 9 | const QUEUE_TIMEOUT_SECS: u16 = 30; 10 | 11 | pub async fn get_queue_timeout_secs(client: &Client, queue_url: &str) -> u16 { 12 | match client 13 | .get_queue_attributes() 14 | .queue_url(queue_url) 15 | .attribute_names(QueueAttributeName::VisibilityTimeout) 16 | .send() 17 | .await 18 | { 19 | Err(e) => { 20 | tracing::warn!("Queue {queue_url}: unable to establish queue timeout, using default {QUEUE_TIMEOUT_SECS} secs: {}", e.to_string()); 21 | QUEUE_TIMEOUT_SECS 22 | } 23 | Ok(gqa) => match gqa.attributes() { 24 | None => { 25 | tracing::debug!( 26 | "Queue {queue_url}: no attrs, using default {QUEUE_TIMEOUT_SECS} secs" 27 | ); 28 | QUEUE_TIMEOUT_SECS 29 | } 30 | Some(attrs) => { 31 | match attrs.get(&QueueAttributeName::VisibilityTimeout) { 32 | None => { 33 | tracing::debug!("Queue {queue_url}: no timeout attr found, using default {QUEUE_TIMEOUT_SECS} secs"); 34 | QUEUE_TIMEOUT_SECS 35 | } 36 | Some(vt) => { 37 | tracing::debug!("Queue {queue_url}: parsing queue tiemout {vt}"); 38 | vt.parse().unwrap_or(QUEUE_TIMEOUT_SECS) 39 | } 40 | } 41 | } 42 | }, 43 | } 44 | } 45 | 46 | pub fn hold_message_lease( 47 | client: &Client, 48 | queue_url: &str, 49 | m: &Message, 50 | timeout_secs: u16, 51 | ) -> Option> { 52 | if let Some(rcpt_handle) = m.receipt_handle().map(|s| s.to_owned()) { 53 | let client = client.clone(); 54 | let queue_url = queue_url.to_owned(); 55 | let msg_id = m.display_id(); 56 | let interval = renewal_interval_for_timeout_secs(timeout_secs); 57 | 58 | let join_handle = tokio::spawn(async move { 59 | let mut ticker = 60 | tokio::time::interval_at(tokio::time::Instant::now() + interval, interval); 61 | loop { 62 | ticker.tick().await; 63 | tracing::info!("Message {msg_id}: renewing lease via {rcpt_handle}"); 64 | let cmv = client 65 | .change_message_visibility() 66 | .queue_url(&queue_url) 67 | .receipt_handle(&rcpt_handle) 68 | .visibility_timeout(timeout_secs.into()) 69 | .send() 70 | .await; 71 | if let Err(e) = cmv { 72 | tracing::error!( 73 | "Message {msg_id}: failed to update lease: {}", 74 | e.to_string() 75 | ); 76 | } 77 | } 78 | }); 79 | 80 | Some(join_handle) 81 | } else { 82 | None 83 | } 84 | } 85 | 86 | fn renewal_interval_for_timeout_secs(timeout_secs: u16) -> tokio::time::Duration { 87 | tokio::time::Duration::from_secs((timeout_secs / 2).into()) 88 | } 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Experimental SQS trigger for Spin 2 | 3 | ## Install the latest release 4 | 5 | The latest stable release of the SQS trigger plugin can be installed like so: 6 | 7 | ```sh 8 | spin plugins update 9 | spin plugins install trigger-sqs 10 | ``` 11 | 12 | ## Install the canary version 13 | 14 | The canary release of the SQS trigger plugin represents the most recent commits on `main` and may not be stable, with some features still in progress. 15 | 16 | ```sh 17 | spin plugins install --url https://github.com/spinframework/spin-trigger-sqs/releases/download/canary/trigger-sqs.json 18 | ``` 19 | 20 | ## Build from source 21 | 22 | You will need Rust and the `pluginify` plugin (`spin plugins install --url https://github.com/itowlson/spin-pluginify/releases/download/canary/pluginify.json`). 23 | 24 | ``` 25 | cargo build --release 26 | spin pluginify --install 27 | ``` 28 | 29 | ## Test 30 | 31 | The trigger uses the AWS configuration environment variables - these must be set before running. 32 | Be sure to set `AWS_DEFAULT_REGION` in your environment to the region of your queue. 33 | 34 | You will also need to change the `queue_url` in `spin.toml` to a queue you have access to. 35 | 36 | ``` 37 | cd guest 38 | spin build --up 39 | ``` 40 | 41 | ## Limitations 42 | 43 | This trigger is currently built using Spin 2.0.1. You will need that version of Spin or above. 44 | 45 | Custom triggers, such as this one, can be run in the Spin command line, but cannot be deployed to Fermyon Cloud. For other hosts, check the documentation. 46 | 47 | ## Configuration 48 | 49 | The SQS trigger uses the AWS credentials from the standard AWS configuration environment variables. These variables must be set before you run `spin up`. The credentials must grant access to all queues that the application wants to monitor. The credentials must allow for reading messages and deleting read messages. 50 | 51 | The trigger assumes that the monitored queues exist: it does not create them. 52 | 53 | ### `spin.toml` 54 | 55 | The trigger type is `sqs`, and there are no application-level configuration options. 56 | 57 | The following options are available to set in the `[[trigger.sqs]]` section: 58 | 59 | | Name | Type | Required? | Description | 60 | |-----------------------|------------------|-----------|-------------| 61 | | `queue_url` | string | required | The queue to which this trigger listens and responds. | 62 | | `max_messages` | number | optional | The maximum number of messages to fetch per AWS queue request. The default is 10. This refers specifically to how messages are fetched from AWS - the component is still invoked separately for each message. | 63 | | `idle_wait_seconds` | number | optional | How long (in seconds) to wait between checks when the queue is idle (i.e. when no messages were received on the last check). The default is 2. If the queue is _not_ idle, there is no wait between checks. The idle wait is also applied if an error occurs. | 64 | | `system_attributes` | array of strings | optional | The list of system-defined [attributes](https://docs.rs/aws-sdk-sqs/latest/aws_sdk_sqs/operation/receive_message/builders/struct.ReceiveMessageFluentBuilder.html#method.set_attribute_names) to fetch and make available to the component. | 65 | | `message_attributes` | array of strings | optional | The list of [message attributes](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-message-metadata.html) to fetch and make available to the component. Only string and binary values are supported. | 66 | | `component` | string or table | required | The component to run when a queue message is received. (This is the standard Spin trigger component field.) | 67 | 68 | For example: 69 | 70 | ```toml 71 | spin_manifest_version = 2 72 | 73 | [application] 74 | name = "test" 75 | version = "0.1.0" 76 | 77 | # One [[trigger.sqs]] section for each queue to monisot1 78 | [[trigger.sqs]] 79 | queue_url = "https://sqs.us-west-2.amazonaws.com/12345/testqueue" 80 | max_messages = 1 81 | system_attributes = ["All"] 82 | component = "test" 83 | 84 | [component.test] 85 | source = "..." 86 | ``` 87 | 88 | ### `spin up` command line options 89 | 90 | There are no custom command line options for this trigger. 91 | 92 | ## Writing SQS components 93 | 94 | There is no SDK for SQS guest components. Use the `sqs.wit` file to generate a trigger binding for your language. Your Wasm component must _export_ the `handle-queue-message` function. See `guest/src/lib.rs` for how to do this in Rust. 95 | 96 | **Note:** In the current WIT, a message has a single `message-attributes` field. This contains both system and message attributes. Feedback is welcome on this design decision. 97 | 98 | Your handler must return a `message-action` (or an error). The `message-action` values are: 99 | 100 | | Name | Description | 101 | |------------|-------------| 102 | | `delete` | The message has been processed and should be removed from the queue. | 103 | | `leave` | The message should be kept on the queue. | 104 | 105 | The trigger renews the message lease for as long as the handler is running. 106 | 107 | **Note:** The current trigger implementation does not make the message visible immediately if the handler returns `leave` or an error; it lets the message become visible through the normal visibility timeout mechanism. 108 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | 3 | name: Build and package 4 | on: 5 | push: 6 | branches: [main] 7 | tags: ["v*"] 8 | pull_request: 9 | branches: [main] 10 | 11 | # TODO: better way? 12 | permissions: 13 | contents: write 14 | 15 | # Construct a concurrency group to be shared across workflow runs. 16 | # The default behavior ensures that only one is running at a time, with 17 | # all others queuing and thus not interrupting runs that are in-flight. 18 | concurrency: ${{ github.workflow }} 19 | 20 | env: 21 | PROGRAM_NAME: trigger-sqs 22 | 23 | jobs: 24 | build: 25 | name: Build plugin 26 | runs-on: ${{ matrix.config.os }} 27 | strategy: 28 | fail-fast: false 29 | matrix: 30 | config: 31 | - { target: "x86_64-unknown-linux-gnu", os: "ubuntu-22.04", arch: "amd64", extension: ""} 32 | - { target: "aarch64-unknown-linux-gnu", os: "ubuntu-22.04", arch: "aarch64", extension: "", extraArg: "--features openssl/vendored" } 33 | - { target: "x86_64-apple-darwin", os: "macos-15-intel", arch: "amd64", extension: "" } 34 | - { target: "aarch64-apple-darwin", os: "macos-14", arch: "aarch64", extension: "" } 35 | - { target: "x86_64-pc-windows-msvc", os: "windows-latest", arch: "amd64", extension: ".exe" } 36 | steps: 37 | - uses: actions/checkout@v3 38 | - name: Install Rust 39 | uses: dtolnay/rust-toolchain@stable 40 | with: 41 | toolchain: 1.88 42 | targets: ${{ matrix.config.target }} 43 | - name: Install Spin 44 | uses: rajatjindal/setup-actions/spin@main 45 | with: 46 | version: "v3.3.0" 47 | - name: Install pluginify 48 | shell: bash 49 | run: spin plugins install --url https://github.com/itowlson/spin-pluginify/releases/download/canary/pluginify.json --yes 50 | - name: Set up for cross-compiled linux aarch64 build 51 | if: matrix.config.target == 'aarch64-unknown-linux-gnu' 52 | run: | 53 | sudo apt update 54 | sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu 55 | echo '[target.aarch64-unknown-linux-gnu]' >> ${HOME}/.cargo/config.toml 56 | echo 'linker = "aarch64-linux-gnu-gcc"' >> ${HOME}/.cargo/config.toml 57 | - name: Build plugin binary 58 | run: cargo build --release --target ${{ matrix.config.target }} ${{ matrix.config.extraArg }} 59 | - name: Run cargo audit 60 | run: | 61 | cargo install cargo-audit --locked 62 | cargo audit 63 | - name: Copy plugin binary to standard location 64 | shell: bash 65 | run: cp target/${{ matrix.config.target }}/release/${{ env.PROGRAM_NAME}}${{ matrix.config.extension }} target/release/${{ env.PROGRAM_NAME}}${{ matrix.config.extension }} 66 | 67 | - name: Pluginify plugin binary 68 | run: spin pluginify --arch ${{ matrix.config.arch }} 69 | - name: Archive pluginified 70 | uses: actions/upload-artifact@v4 71 | with: 72 | name: ${{ env.PROGRAM_NAME}}-${{ matrix.config.os }}-${{ matrix.config.arch }} 73 | path: | 74 | *.tar.gz 75 | *.json 76 | 77 | package: 78 | name: Package plugin 79 | if: github.event_name == 'push' 80 | runs-on: ubuntu-latest 81 | needs: build 82 | steps: 83 | - name: Install Spin 84 | uses: rajatjindal/setup-actions/spin@main 85 | with: 86 | version: "v2.0.0" 87 | - name: Install pluginify 88 | shell: bash 89 | run: spin plugins install --url https://github.com/itowlson/spin-pluginify/releases/download/canary/pluginify.json --yes 90 | 91 | - name: set the release version (tag) 92 | if: startsWith(github.ref, 'refs/tags/v') 93 | shell: bash 94 | run: echo "RELEASE_VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV 95 | - name: set the release version (main) 96 | if: github.ref == 'refs/heads/main' 97 | shell: bash 98 | run: echo "RELEASE_VERSION=canary" >> $GITHUB_ENV 99 | - name: set the release version (TEST TEST TEST) 100 | if: github.event_name == 'pull_request' 101 | shell: bash 102 | run: echo "RELEASE_VERSION=precanary" >> $GITHUB_ENV 103 | 104 | - name: Download artifacts 105 | uses: actions/download-artifact@v4 106 | with: 107 | pattern: ${{ env.PROGRAM_NAME}}-* 108 | - name: Display structure of downloaded files 109 | run: ls -R 110 | - name: pluginify it 111 | run: | 112 | spin pluginify --merge --release-url-base https://github.com/spinframework/spin-trigger-sqs/releases/download/${{ env.RELEASE_VERSION }}/ >${{ env.PROGRAM_NAME }}.json 113 | - name: Display merged manifest 114 | run: cat ${{ env.PROGRAM_NAME }}.json 115 | 116 | # Handle versioned release 117 | - name: Upload tars to Github release 118 | if: startsWith(github.ref, 'refs/tags/v') 119 | uses: svenstaro/upload-release-action@v2 120 | with: 121 | repo_token: ${{ secrets.GITHUB_TOKEN }} 122 | file: "**/*.tar.gz" 123 | file_glob: true 124 | tag: ${{ github.ref }} 125 | - name: Upload manifest to Github release 126 | if: startsWith(github.ref, 'refs/tags/v') 127 | uses: svenstaro/upload-release-action@v2 128 | with: 129 | repo_token: ${{ secrets.GITHUB_TOKEN }} 130 | file: ${{ env.PROGRAM_NAME }}.json 131 | tag: ${{ github.ref }} 132 | 133 | # Handle canary release 134 | - name: Delete canary tag 135 | if: github.ref == 'refs/heads/main' 136 | uses: dev-drprasad/delete-tag-and-release@v0.2.1 137 | env: 138 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 139 | with: 140 | tag_name: canary 141 | - name: Recreate canary tag and release 142 | if: github.ref == 'refs/heads/main' 143 | uses: ncipollo/release-action@v1.10.0 144 | with: 145 | tag: canary 146 | allowUpdates: true 147 | prerelease: true 148 | - name: Upload tars to Github release 149 | if: github.ref == 'refs/heads/main' 150 | uses: svenstaro/upload-release-action@v2 151 | with: 152 | repo_token: ${{ secrets.GITHUB_TOKEN }} 153 | file: "**/*.tar.gz" 154 | file_glob: true 155 | tag: canary 156 | - name: Upload manifest to Github release 157 | if: github.ref == 'refs/heads/main' 158 | uses: svenstaro/upload-release-action@v2 159 | with: 160 | repo_token: ${{ secrets.GITHUB_TOKEN }} 161 | file: ${{ env.PROGRAM_NAME }}.json 162 | tag: canary 163 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright (c) The Spin Framework Contributors. All Rights Reserved. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | --- LLVM Exceptions to the Apache 2.0 License ---- 204 | 205 | As an exception, if, as a result of your compiling your source code, portions 206 | of this Software are embedded into an Object form of such source code, you 207 | may redistribute such embedded portions in such Object form without complying 208 | with the conditions of Sections 4(a), 4(b) and 4(d) of the License. 209 | 210 | In addition, if you combine or link compiled forms of this Software with 211 | software that is licensed under the GPLv2 ("Combined Software") and if a 212 | court of competent jurisdiction determines that the patent provision (Section 213 | 3), the indemnity provision (Section 9) or other Section of the License 214 | conflicts with the conditions of the GPLv2, you may retroactively and 215 | prospectively choose to deem waived or otherwise exclude such Section(s) of 216 | the License, but only in their entirety and only with respect to the Combined 217 | Software. 218 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, sync::Arc}; 2 | 3 | use anyhow::Result; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | mod aws; 7 | mod utils; 8 | 9 | use spin_factors::RuntimeFactors; 10 | use spin_trigger::{cli::NoCliArgs, App, Trigger, TriggerApp}; 11 | use utils::MessageUtils; 12 | 13 | wasmtime::component::bindgen!({ 14 | path: "sqs.wit", 15 | imports: { default: async }, 16 | exports: { default: async }, 17 | }); 18 | 19 | use fermyon::spin_sqs::sqs_types as sqs; 20 | 21 | pub struct SqsTrigger { 22 | queue_components: Vec, 23 | } 24 | 25 | #[derive(Clone, Debug, Default, Deserialize, Serialize)] 26 | #[serde(deny_unknown_fields)] 27 | pub struct SqsTriggerConfig { 28 | pub component: String, 29 | pub queue_url: String, 30 | pub max_messages: Option, 31 | pub idle_wait_seconds: Option, 32 | pub system_attributes: Option>, 33 | pub message_attributes: Option>, 34 | } 35 | 36 | #[derive(Clone, Debug)] 37 | struct Component { 38 | pub id: String, 39 | pub queue_url: String, 40 | pub max_messages: u16, 41 | pub idle_wait: tokio::time::Duration, 42 | pub system_attributes: Vec, 43 | pub message_attributes: Vec, 44 | } 45 | 46 | #[derive(Clone, Debug, Default, Deserialize, Serialize)] 47 | #[serde(deny_unknown_fields)] 48 | struct TriggerMetadata { 49 | r#type: String, 50 | } 51 | 52 | // This is a placeholder - we don't yet detect any situations that would require 53 | // graceful or ungraceful exit. It will likely require rework when we do. It 54 | // is here so that we have a skeleton for returning errors that doesn't expose 55 | // us to thoughtlessly "?"-ing away an Err case and creating a situation where a 56 | // transient failure could end the trigger. 57 | #[allow(dead_code)] 58 | #[derive(Debug)] 59 | enum TerminationReason { 60 | ExitRequested, 61 | Other(String), 62 | } 63 | 64 | impl Trigger for SqsTrigger { 65 | const TYPE: &'static str = "sqs"; 66 | type CliArgs = NoCliArgs; 67 | 68 | type InstanceState = (); 69 | 70 | fn new(_cli_args: Self::CliArgs, app: &App) -> anyhow::Result { 71 | let queue_components = app 72 | .trigger_configs::(>::TYPE)? 73 | .into_iter() 74 | .map(|(_, config)| Component { 75 | id: config.component.clone(), 76 | queue_url: config.queue_url.clone(), 77 | max_messages: config.max_messages.unwrap_or(10), 78 | idle_wait: tokio::time::Duration::from_secs(config.idle_wait_seconds.unwrap_or(2)), 79 | system_attributes: config 80 | .system_attributes 81 | .clone() 82 | .unwrap_or_default() 83 | .iter() 84 | .map(|s| s.as_str().into()) 85 | .collect(), 86 | message_attributes: config.message_attributes.clone().unwrap_or_default(), 87 | }) 88 | .collect(); 89 | 90 | Ok(Self { queue_components }) 91 | } 92 | 93 | async fn run(self, trigger_app: TriggerApp) -> Result<()> { 94 | tokio::spawn(async move { 95 | tokio::signal::ctrl_c().await.unwrap(); 96 | std::process::exit(0); 97 | }); 98 | 99 | let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await; 100 | 101 | let client = aws::Client::new(&config); 102 | let app = Arc::new(trigger_app); 103 | 104 | let loops = self 105 | .queue_components 106 | .iter() 107 | .map(|component| Self::start_receive_loop(app.clone(), &client, component)); 108 | 109 | let (tr, _, rest) = futures::future::select_all(loops).await; 110 | drop(rest); 111 | 112 | match tr { 113 | Ok(TerminationReason::ExitRequested) => { 114 | tracing::trace!("Exiting"); 115 | Ok(()) 116 | } 117 | _ => { 118 | tracing::trace!("Fatal: {:?}", tr); 119 | Err(anyhow::anyhow!("{tr:?}")) 120 | } 121 | } 122 | } 123 | } 124 | 125 | impl SqsTrigger { 126 | fn start_receive_loop( 127 | app: Arc>, 128 | client: &aws::Client, 129 | component: &Component, 130 | ) -> tokio::task::JoinHandle { 131 | let future = Self::receive(app, client.clone(), component.clone()); 132 | tokio::task::spawn(future) 133 | } 134 | 135 | // This doesn't return a Result because we don't want a thoughtless `?` to exit the loop 136 | // and terminate the entire trigger. Termination should be a conscious decision when 137 | // we are sure there is no point continuing. 138 | async fn receive( 139 | app: Arc>, 140 | client: aws::Client, 141 | component: Component, 142 | ) -> TerminationReason { 143 | let queue_timeout_secs = aws::get_queue_timeout_secs(&client, &component.queue_url).await; 144 | 145 | loop { 146 | tracing::trace!( 147 | "Queue {}: attempting to receive up to {}", 148 | component.queue_url, 149 | component.max_messages 150 | ); 151 | 152 | let rmo = match client 153 | .receive_message() 154 | .queue_url(&component.queue_url) 155 | .max_number_of_messages(i32::from(component.max_messages)) 156 | .set_message_system_attribute_names(Some(component.system_attributes.clone())) 157 | .set_message_attribute_names(Some(component.message_attributes.clone())) 158 | .send() 159 | .await 160 | { 161 | Ok(rmo) => rmo, 162 | Err(e) => { 163 | tracing::error!( 164 | "Queue {}: error receiving messages: {:?}", 165 | component.queue_url, 166 | e 167 | ); 168 | tokio::time::sleep(component.idle_wait).await; 169 | continue; 170 | } 171 | }; 172 | 173 | if let Some(msgs) = rmo.messages { 174 | let msgs = msgs.to_vec(); 175 | tracing::info!( 176 | "Queue {}: received {} message(s)", 177 | component.queue_url, 178 | msgs.len() 179 | ); 180 | for msg in msgs { 181 | // Spin off the execution so it doesn't block the queue 182 | let processor = 183 | SqsMessageProcessor::new(&app, &client, &component, queue_timeout_secs); 184 | tokio::spawn(async move { processor.process_message(msg).await }); 185 | } 186 | } else { 187 | tracing::trace!("Queue {}: no messages received", component.queue_url); 188 | tokio::time::sleep(component.idle_wait).await; 189 | } 190 | } 191 | } 192 | } 193 | 194 | struct SqsMessageProcessor { 195 | app: Arc>, 196 | client: aws::Client, 197 | component: Component, 198 | queue_timeout_secs: u16, 199 | } 200 | 201 | impl SqsMessageProcessor { 202 | fn new( 203 | app: &Arc>, 204 | client: &aws::Client, 205 | component: &Component, 206 | queue_timeout_secs: u16, 207 | ) -> Self { 208 | Self { 209 | app: app.clone(), 210 | client: client.clone(), 211 | component: component.clone(), 212 | queue_timeout_secs, 213 | } 214 | } 215 | 216 | async fn process_message(&self, msg: aws::Message) { 217 | let msg_id = msg.display_id(); 218 | tracing::trace!("Message {msg_id}: spawned processing task"); 219 | 220 | // The attr lists have to be returned to this level so that they live long enough 221 | let attrs = to_wit_message_attrs(&msg); 222 | let message = sqs::Message { 223 | id: msg.message_id().map(|s| s.to_owned()), 224 | message_attributes: attrs, 225 | body: msg.body().map(|s| s.to_owned()), 226 | }; 227 | 228 | let renew_lease = aws::hold_message_lease( 229 | &self.client, 230 | self.queue_url(), 231 | &msg, 232 | self.queue_timeout_secs, 233 | ); 234 | 235 | let action = self.execute_wasm(message).await; 236 | 237 | if let Some(renewer) = renew_lease { 238 | renewer.abort(); 239 | } 240 | 241 | match action { 242 | Ok(sqs::MessageAction::Delete) => { 243 | tracing::trace!("Message {msg_id} processed successfully: action is Delete"); 244 | if let Some(receipt_handle) = msg.receipt_handle() { 245 | tracing::trace!("Message {msg_id}: attempting to delete via {receipt_handle}"); 246 | match self 247 | .client 248 | .delete_message() 249 | .queue_url(self.queue_url()) 250 | .receipt_handle(receipt_handle) 251 | .send() 252 | .await 253 | { 254 | Ok(_) => tracing::trace!("Message {msg_id} deleted"), 255 | Err(e) => tracing::error!( 256 | "Message {msg_id}: error deleting via {receipt_handle}: {e:?}" 257 | ), 258 | } 259 | } 260 | } 261 | Ok(sqs::MessageAction::Leave) => { 262 | tracing::trace!("Message {msg_id} processed successfully: action is Leave"); 263 | // TODO: change message visibility to 0? 264 | } 265 | Err(e) => { 266 | tracing::error!("Message {msg_id} processing error: {}", e.to_string()); 267 | // TODO: change message visibility to 0 I guess? 268 | } 269 | } 270 | } 271 | 272 | async fn execute_wasm(&self, message: sqs::Message) -> Result { 273 | let msg_id = message.display_id(); 274 | let component_id = &self.component.id; 275 | tracing::trace!("Message {msg_id}: executing component {component_id}"); 276 | let instance_builder = self.app.prepare(component_id)?; 277 | let (instance, mut store) = instance_builder.instantiate(()).await?; 278 | 279 | let instance = SpinSqs::new(&mut store, &instance)?; 280 | 281 | match instance 282 | .call_handle_queue_message(&mut store, &message) 283 | .await 284 | { 285 | Ok(Ok(action)) => { 286 | tracing::trace!("Message {msg_id}: component {component_id} completed okay"); 287 | Ok(action) 288 | } 289 | Ok(Err(e)) => { 290 | tracing::warn!( 291 | "Message {msg_id}: component {component_id} returned error {:?}", 292 | e 293 | ); 294 | Err(anyhow::anyhow!( 295 | "Component {component_id} returned error processing message {msg_id}" 296 | )) // TODO: more details when WIT provides them 297 | } 298 | Err(e) => { 299 | tracing::error!( 300 | "Message {msg_id}: engine error running component {component_id}: {:?}", 301 | e 302 | ); 303 | Err(anyhow::anyhow!( 304 | "Error executing component {component_id} while processing message {msg_id}" 305 | )) 306 | } 307 | } 308 | } 309 | 310 | fn queue_url(&self) -> &str { 311 | &self.component.queue_url 312 | } 313 | } 314 | 315 | fn to_wit_message_attrs(m: &aws::Message) -> Vec { 316 | let msg_id = m.display_id(); 317 | 318 | let sysattrs = m 319 | .attributes() 320 | .map(system_attributes_to_wit) 321 | .unwrap_or_default(); 322 | let userattrs = m 323 | .message_attributes() 324 | .map(|a| user_attributes_to_wit(a, &msg_id)) 325 | .unwrap_or_default(); 326 | 327 | [sysattrs, userattrs].concat() 328 | } 329 | 330 | fn system_attributes_to_wit( 331 | src: &HashMap, 332 | ) -> Vec { 333 | src.iter() 334 | .map(|(k, v)| sqs::MessageAttribute { 335 | name: k.as_str().to_string(), 336 | value: sqs::MessageAttributeValue::Str(v.to_string()), 337 | data_type: None, 338 | }) 339 | .collect::>() 340 | } 341 | 342 | fn user_attributes_to_wit( 343 | src: &HashMap, 344 | msg_id: &str, 345 | ) -> Vec { 346 | src.iter() 347 | .filter_map(|(k, v)| { 348 | match wit_value(v) { 349 | Ok(wv) => Some(sqs::MessageAttribute { 350 | name: k.to_string(), 351 | value: wv, 352 | data_type: None, 353 | }), 354 | Err(e) => { 355 | tracing::error!("Message {msg_id}: can't convert attribute {} to string or blob, skipped: {e:?}", k.as_str()); // TODO: this should probably fail the message 356 | None 357 | } 358 | } 359 | }) 360 | .collect::>() 361 | } 362 | 363 | fn wit_value(v: &aws::MessageAttributeValue) -> Result { 364 | if let Some(s) = v.string_value() { 365 | Ok(sqs::MessageAttributeValue::Str(s.to_string())) 366 | } else if let Some(b) = v.binary_value() { 367 | Ok(sqs::MessageAttributeValue::Binary(b.as_ref().to_vec())) 368 | } else { 369 | Err(anyhow::anyhow!( 370 | "Don't know what to do with message attribute value {:?} (data type {:?})", 371 | v, 372 | v.data_type() 373 | )) 374 | } 375 | } 376 | -------------------------------------------------------------------------------- /guest/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "android-tzdata" 7 | version = "0.1.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 10 | 11 | [[package]] 12 | name = "android_system_properties" 13 | version = "0.1.5" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 16 | dependencies = [ 17 | "libc", 18 | ] 19 | 20 | [[package]] 21 | name = "anyhow" 22 | version = "1.0.75" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" 25 | 26 | [[package]] 27 | name = "arrayvec" 28 | version = "0.7.6" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" 31 | 32 | [[package]] 33 | name = "async-trait" 34 | version = "0.1.74" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" 37 | dependencies = [ 38 | "proc-macro2", 39 | "quote", 40 | "syn 2.0.106", 41 | ] 42 | 43 | [[package]] 44 | name = "autocfg" 45 | version = "1.1.0" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 48 | 49 | [[package]] 50 | name = "base64" 51 | version = "0.22.1" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 54 | 55 | [[package]] 56 | name = "bitflags" 57 | version = "2.4.1" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" 60 | 61 | [[package]] 62 | name = "block-buffer" 63 | version = "0.10.4" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 66 | dependencies = [ 67 | "generic-array", 68 | ] 69 | 70 | [[package]] 71 | name = "bumpalo" 72 | version = "3.19.0" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" 75 | 76 | [[package]] 77 | name = "byteorder" 78 | version = "1.5.0" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 81 | 82 | [[package]] 83 | name = "bytes" 84 | version = "1.3.0" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" 87 | 88 | [[package]] 89 | name = "cc" 90 | version = "1.2.34" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" 93 | dependencies = [ 94 | "shlex", 95 | ] 96 | 97 | [[package]] 98 | name = "cfg-if" 99 | version = "1.0.3" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" 102 | 103 | [[package]] 104 | name = "chrono" 105 | version = "0.4.41" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" 108 | dependencies = [ 109 | "android-tzdata", 110 | "iana-time-zone", 111 | "js-sys", 112 | "num-traits", 113 | "wasm-bindgen", 114 | "windows-link", 115 | ] 116 | 117 | [[package]] 118 | name = "core-foundation-sys" 119 | version = "0.8.7" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 122 | 123 | [[package]] 124 | name = "cpufeatures" 125 | version = "0.2.17" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" 128 | dependencies = [ 129 | "libc", 130 | ] 131 | 132 | [[package]] 133 | name = "crypto-common" 134 | version = "0.1.6" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 137 | dependencies = [ 138 | "generic-array", 139 | "typenum", 140 | ] 141 | 142 | [[package]] 143 | name = "digest" 144 | version = "0.10.7" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 147 | dependencies = [ 148 | "block-buffer", 149 | "crypto-common", 150 | "subtle", 151 | ] 152 | 153 | [[package]] 154 | name = "equivalent" 155 | version = "1.0.1" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 158 | 159 | [[package]] 160 | name = "fallible-iterator" 161 | version = "0.2.0" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" 164 | 165 | [[package]] 166 | name = "fnv" 167 | version = "1.0.7" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 170 | 171 | [[package]] 172 | name = "foldhash" 173 | version = "0.1.5" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 176 | 177 | [[package]] 178 | name = "form_urlencoded" 179 | version = "1.1.0" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" 182 | dependencies = [ 183 | "percent-encoding", 184 | ] 185 | 186 | [[package]] 187 | name = "futures" 188 | version = "0.3.31" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 191 | dependencies = [ 192 | "futures-channel", 193 | "futures-core", 194 | "futures-executor", 195 | "futures-io", 196 | "futures-sink", 197 | "futures-task", 198 | "futures-util", 199 | ] 200 | 201 | [[package]] 202 | name = "futures-channel" 203 | version = "0.3.31" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 206 | dependencies = [ 207 | "futures-core", 208 | "futures-sink", 209 | ] 210 | 211 | [[package]] 212 | name = "futures-core" 213 | version = "0.3.31" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 216 | 217 | [[package]] 218 | name = "futures-executor" 219 | version = "0.3.31" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 222 | dependencies = [ 223 | "futures-core", 224 | "futures-task", 225 | "futures-util", 226 | ] 227 | 228 | [[package]] 229 | name = "futures-io" 230 | version = "0.3.31" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 233 | 234 | [[package]] 235 | name = "futures-macro" 236 | version = "0.3.31" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 239 | dependencies = [ 240 | "proc-macro2", 241 | "quote", 242 | "syn 2.0.106", 243 | ] 244 | 245 | [[package]] 246 | name = "futures-sink" 247 | version = "0.3.31" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 250 | 251 | [[package]] 252 | name = "futures-task" 253 | version = "0.3.31" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 256 | 257 | [[package]] 258 | name = "futures-util" 259 | version = "0.3.31" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 262 | dependencies = [ 263 | "futures-channel", 264 | "futures-core", 265 | "futures-io", 266 | "futures-macro", 267 | "futures-sink", 268 | "futures-task", 269 | "memchr", 270 | "pin-project-lite", 271 | "pin-utils", 272 | "slab", 273 | ] 274 | 275 | [[package]] 276 | name = "generic-array" 277 | version = "0.14.7" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 280 | dependencies = [ 281 | "typenum", 282 | "version_check", 283 | ] 284 | 285 | [[package]] 286 | name = "getrandom" 287 | version = "0.3.3" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" 290 | dependencies = [ 291 | "cfg-if", 292 | "libc", 293 | "r-efi", 294 | "wasi 0.14.3+wasi-0.2.4", 295 | ] 296 | 297 | [[package]] 298 | name = "guest" 299 | version = "0.1.0" 300 | dependencies = [ 301 | "anyhow", 302 | "bytes", 303 | "http 0.2.8", 304 | "spin-sdk", 305 | "wit-bindgen 0.43.0", 306 | ] 307 | 308 | [[package]] 309 | name = "hashbrown" 310 | version = "0.15.5" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 313 | dependencies = [ 314 | "foldhash", 315 | ] 316 | 317 | [[package]] 318 | name = "heck" 319 | version = "0.5.0" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 322 | 323 | [[package]] 324 | name = "hmac" 325 | version = "0.12.1" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" 328 | dependencies = [ 329 | "digest", 330 | ] 331 | 332 | [[package]] 333 | name = "http" 334 | version = "0.2.8" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" 337 | dependencies = [ 338 | "bytes", 339 | "fnv", 340 | "itoa", 341 | ] 342 | 343 | [[package]] 344 | name = "http" 345 | version = "1.1.0" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" 348 | dependencies = [ 349 | "bytes", 350 | "fnv", 351 | "itoa", 352 | ] 353 | 354 | [[package]] 355 | name = "iana-time-zone" 356 | version = "0.1.63" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" 359 | dependencies = [ 360 | "android_system_properties", 361 | "core-foundation-sys", 362 | "iana-time-zone-haiku", 363 | "js-sys", 364 | "log", 365 | "wasm-bindgen", 366 | "windows-core", 367 | ] 368 | 369 | [[package]] 370 | name = "iana-time-zone-haiku" 371 | version = "0.1.2" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 374 | dependencies = [ 375 | "cc", 376 | ] 377 | 378 | [[package]] 379 | name = "id-arena" 380 | version = "2.2.1" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" 383 | 384 | [[package]] 385 | name = "indexmap" 386 | version = "2.11.0" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" 389 | dependencies = [ 390 | "equivalent", 391 | "hashbrown", 392 | "serde", 393 | ] 394 | 395 | [[package]] 396 | name = "itoa" 397 | version = "1.0.5" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" 400 | 401 | [[package]] 402 | name = "js-sys" 403 | version = "0.3.77" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 406 | dependencies = [ 407 | "once_cell", 408 | "wasm-bindgen", 409 | ] 410 | 411 | [[package]] 412 | name = "leb128fmt" 413 | version = "0.1.0" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" 416 | 417 | [[package]] 418 | name = "libc" 419 | version = "0.2.175" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" 422 | 423 | [[package]] 424 | name = "log" 425 | version = "0.4.20" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 428 | 429 | [[package]] 430 | name = "md-5" 431 | version = "0.10.6" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" 434 | dependencies = [ 435 | "cfg-if", 436 | "digest", 437 | ] 438 | 439 | [[package]] 440 | name = "memchr" 441 | version = "2.5.0" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 444 | 445 | [[package]] 446 | name = "num-traits" 447 | version = "0.2.19" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 450 | dependencies = [ 451 | "autocfg", 452 | ] 453 | 454 | [[package]] 455 | name = "once_cell" 456 | version = "1.21.3" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 459 | 460 | [[package]] 461 | name = "percent-encoding" 462 | version = "2.2.0" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" 465 | 466 | [[package]] 467 | name = "pin-project-lite" 468 | version = "0.2.13" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" 471 | 472 | [[package]] 473 | name = "pin-utils" 474 | version = "0.1.0" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 477 | 478 | [[package]] 479 | name = "postgres-protocol" 480 | version = "0.6.8" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "76ff0abab4a9b844b93ef7b81f1efc0a366062aaef2cd702c76256b5dc075c54" 483 | dependencies = [ 484 | "base64", 485 | "byteorder", 486 | "bytes", 487 | "fallible-iterator", 488 | "hmac", 489 | "md-5", 490 | "memchr", 491 | "rand", 492 | "sha2", 493 | "stringprep", 494 | ] 495 | 496 | [[package]] 497 | name = "postgres-types" 498 | version = "0.2.9" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "613283563cd90e1dfc3518d548caee47e0e725455ed619881f5cf21f36de4b48" 501 | dependencies = [ 502 | "bytes", 503 | "fallible-iterator", 504 | "postgres-protocol", 505 | ] 506 | 507 | [[package]] 508 | name = "postgres_range" 509 | version = "0.11.1" 510 | source = "registry+https://github.com/rust-lang/crates.io-index" 511 | checksum = "f6dce28dc5ba143d8eb157b62aac01ae5a1c585c40792158b720e86a87642101" 512 | dependencies = [ 513 | "postgres-protocol", 514 | "postgres-types", 515 | ] 516 | 517 | [[package]] 518 | name = "ppv-lite86" 519 | version = "0.2.21" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 522 | dependencies = [ 523 | "zerocopy", 524 | ] 525 | 526 | [[package]] 527 | name = "prettyplease" 528 | version = "0.2.37" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" 531 | dependencies = [ 532 | "proc-macro2", 533 | "syn 2.0.106", 534 | ] 535 | 536 | [[package]] 537 | name = "proc-macro2" 538 | version = "1.0.101" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" 541 | dependencies = [ 542 | "unicode-ident", 543 | ] 544 | 545 | [[package]] 546 | name = "quote" 547 | version = "1.0.40" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 550 | dependencies = [ 551 | "proc-macro2", 552 | ] 553 | 554 | [[package]] 555 | name = "r-efi" 556 | version = "5.3.0" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 559 | 560 | [[package]] 561 | name = "rand" 562 | version = "0.9.2" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" 565 | dependencies = [ 566 | "rand_chacha", 567 | "rand_core", 568 | ] 569 | 570 | [[package]] 571 | name = "rand_chacha" 572 | version = "0.9.0" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 575 | dependencies = [ 576 | "ppv-lite86", 577 | "rand_core", 578 | ] 579 | 580 | [[package]] 581 | name = "rand_core" 582 | version = "0.9.3" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" 585 | dependencies = [ 586 | "getrandom", 587 | ] 588 | 589 | [[package]] 590 | name = "routefinder" 591 | version = "0.5.3" 592 | source = "registry+https://github.com/rust-lang/crates.io-index" 593 | checksum = "94f8f99b10dedd317514253dda1fa7c14e344aac96e1f78149a64879ce282aca" 594 | dependencies = [ 595 | "smartcow", 596 | "smartstring", 597 | ] 598 | 599 | [[package]] 600 | name = "rust_decimal" 601 | version = "1.37.2" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "b203a6425500a03e0919c42d3c47caca51e79f1132046626d2c8871c5092035d" 604 | dependencies = [ 605 | "arrayvec", 606 | "num-traits", 607 | ] 608 | 609 | [[package]] 610 | name = "rustversion" 611 | version = "1.0.22" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" 614 | 615 | [[package]] 616 | name = "ryu" 617 | version = "1.0.15" 618 | source = "registry+https://github.com/rust-lang/crates.io-index" 619 | checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" 620 | 621 | [[package]] 622 | name = "semver" 623 | version = "1.0.20" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" 626 | 627 | [[package]] 628 | name = "serde" 629 | version = "1.0.192" 630 | source = "registry+https://github.com/rust-lang/crates.io-index" 631 | checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" 632 | dependencies = [ 633 | "serde_derive", 634 | ] 635 | 636 | [[package]] 637 | name = "serde_derive" 638 | version = "1.0.192" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" 641 | dependencies = [ 642 | "proc-macro2", 643 | "quote", 644 | "syn 2.0.106", 645 | ] 646 | 647 | [[package]] 648 | name = "serde_json" 649 | version = "1.0.108" 650 | source = "registry+https://github.com/rust-lang/crates.io-index" 651 | checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" 652 | dependencies = [ 653 | "itoa", 654 | "ryu", 655 | "serde", 656 | ] 657 | 658 | [[package]] 659 | name = "sha2" 660 | version = "0.10.9" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" 663 | dependencies = [ 664 | "cfg-if", 665 | "cpufeatures", 666 | "digest", 667 | ] 668 | 669 | [[package]] 670 | name = "shlex" 671 | version = "1.3.0" 672 | source = "registry+https://github.com/rust-lang/crates.io-index" 673 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 674 | 675 | [[package]] 676 | name = "slab" 677 | version = "0.4.9" 678 | source = "registry+https://github.com/rust-lang/crates.io-index" 679 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 680 | dependencies = [ 681 | "autocfg", 682 | ] 683 | 684 | [[package]] 685 | name = "smartcow" 686 | version = "0.2.1" 687 | source = "registry+https://github.com/rust-lang/crates.io-index" 688 | checksum = "656fcb1c1fca8c4655372134ce87d8afdf5ec5949ebabe8d314be0141d8b5da2" 689 | dependencies = [ 690 | "smartstring", 691 | ] 692 | 693 | [[package]] 694 | name = "smartstring" 695 | version = "1.0.1" 696 | source = "registry+https://github.com/rust-lang/crates.io-index" 697 | checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" 698 | dependencies = [ 699 | "autocfg", 700 | "static_assertions", 701 | "version_check", 702 | ] 703 | 704 | [[package]] 705 | name = "spin-executor" 706 | version = "5.0.0" 707 | source = "registry+https://github.com/rust-lang/crates.io-index" 708 | checksum = "cde323c004c7d9d68fbccd1dd6caee6330aaefbcb40562587fc0356bd0ea8e5f" 709 | dependencies = [ 710 | "futures", 711 | "once_cell", 712 | "wasi 0.13.1+wasi-0.2.0", 713 | ] 714 | 715 | [[package]] 716 | name = "spin-macro" 717 | version = "5.0.0" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "c1592d94530c032aa8676c8adee71022fba49504ae6d4d8961a454029d3bac6f" 720 | dependencies = [ 721 | "anyhow", 722 | "bytes", 723 | "proc-macro2", 724 | "quote", 725 | "syn 1.0.107", 726 | ] 727 | 728 | [[package]] 729 | name = "spin-sdk" 730 | version = "5.0.0" 731 | source = "registry+https://github.com/rust-lang/crates.io-index" 732 | checksum = "72e9705e63f1e7f955b3c9bef754a6f957624d734c6091990a2dc6f1e135625d" 733 | dependencies = [ 734 | "anyhow", 735 | "async-trait", 736 | "bytes", 737 | "chrono", 738 | "form_urlencoded", 739 | "futures", 740 | "http 1.1.0", 741 | "once_cell", 742 | "postgres_range", 743 | "routefinder", 744 | "rust_decimal", 745 | "serde", 746 | "serde_json", 747 | "spin-executor", 748 | "spin-macro", 749 | "thiserror", 750 | "uuid", 751 | "wasi 0.13.1+wasi-0.2.0", 752 | "wit-bindgen 0.43.0", 753 | ] 754 | 755 | [[package]] 756 | name = "static_assertions" 757 | version = "1.1.0" 758 | source = "registry+https://github.com/rust-lang/crates.io-index" 759 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 760 | 761 | [[package]] 762 | name = "stringprep" 763 | version = "0.1.5" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" 766 | dependencies = [ 767 | "unicode-bidi", 768 | "unicode-normalization", 769 | "unicode-properties", 770 | ] 771 | 772 | [[package]] 773 | name = "subtle" 774 | version = "2.6.1" 775 | source = "registry+https://github.com/rust-lang/crates.io-index" 776 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 777 | 778 | [[package]] 779 | name = "syn" 780 | version = "1.0.107" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" 783 | dependencies = [ 784 | "proc-macro2", 785 | "quote", 786 | "unicode-ident", 787 | ] 788 | 789 | [[package]] 790 | name = "syn" 791 | version = "2.0.106" 792 | source = "registry+https://github.com/rust-lang/crates.io-index" 793 | checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" 794 | dependencies = [ 795 | "proc-macro2", 796 | "quote", 797 | "unicode-ident", 798 | ] 799 | 800 | [[package]] 801 | name = "thiserror" 802 | version = "1.0.38" 803 | source = "registry+https://github.com/rust-lang/crates.io-index" 804 | checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" 805 | dependencies = [ 806 | "thiserror-impl", 807 | ] 808 | 809 | [[package]] 810 | name = "thiserror-impl" 811 | version = "1.0.38" 812 | source = "registry+https://github.com/rust-lang/crates.io-index" 813 | checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" 814 | dependencies = [ 815 | "proc-macro2", 816 | "quote", 817 | "syn 1.0.107", 818 | ] 819 | 820 | [[package]] 821 | name = "tinyvec" 822 | version = "1.10.0" 823 | source = "registry+https://github.com/rust-lang/crates.io-index" 824 | checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" 825 | dependencies = [ 826 | "tinyvec_macros", 827 | ] 828 | 829 | [[package]] 830 | name = "tinyvec_macros" 831 | version = "0.1.1" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 834 | 835 | [[package]] 836 | name = "typenum" 837 | version = "1.18.0" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" 840 | 841 | [[package]] 842 | name = "unicode-bidi" 843 | version = "0.3.18" 844 | source = "registry+https://github.com/rust-lang/crates.io-index" 845 | checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" 846 | 847 | [[package]] 848 | name = "unicode-ident" 849 | version = "1.0.6" 850 | source = "registry+https://github.com/rust-lang/crates.io-index" 851 | checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" 852 | 853 | [[package]] 854 | name = "unicode-normalization" 855 | version = "0.1.24" 856 | source = "registry+https://github.com/rust-lang/crates.io-index" 857 | checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" 858 | dependencies = [ 859 | "tinyvec", 860 | ] 861 | 862 | [[package]] 863 | name = "unicode-properties" 864 | version = "0.1.3" 865 | source = "registry+https://github.com/rust-lang/crates.io-index" 866 | checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" 867 | 868 | [[package]] 869 | name = "unicode-xid" 870 | version = "0.2.4" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" 873 | 874 | [[package]] 875 | name = "uuid" 876 | version = "1.18.0" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" 879 | dependencies = [ 880 | "js-sys", 881 | "wasm-bindgen", 882 | ] 883 | 884 | [[package]] 885 | name = "version_check" 886 | version = "0.9.4" 887 | source = "registry+https://github.com/rust-lang/crates.io-index" 888 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 889 | 890 | [[package]] 891 | name = "wasi" 892 | version = "0.13.1+wasi-0.2.0" 893 | source = "registry+https://github.com/rust-lang/crates.io-index" 894 | checksum = "2f43d1c36145feb89a3e61aa0ba3e582d976a8ab77f1474aa0adb80800fe0cf8" 895 | dependencies = [ 896 | "wit-bindgen-rt 0.24.0", 897 | ] 898 | 899 | [[package]] 900 | name = "wasi" 901 | version = "0.14.3+wasi-0.2.4" 902 | source = "registry+https://github.com/rust-lang/crates.io-index" 903 | checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" 904 | dependencies = [ 905 | "wit-bindgen 0.45.0", 906 | ] 907 | 908 | [[package]] 909 | name = "wasm-bindgen" 910 | version = "0.2.100" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 913 | dependencies = [ 914 | "cfg-if", 915 | "once_cell", 916 | "rustversion", 917 | "wasm-bindgen-macro", 918 | ] 919 | 920 | [[package]] 921 | name = "wasm-bindgen-backend" 922 | version = "0.2.100" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 925 | dependencies = [ 926 | "bumpalo", 927 | "log", 928 | "proc-macro2", 929 | "quote", 930 | "syn 2.0.106", 931 | "wasm-bindgen-shared", 932 | ] 933 | 934 | [[package]] 935 | name = "wasm-bindgen-macro" 936 | version = "0.2.100" 937 | source = "registry+https://github.com/rust-lang/crates.io-index" 938 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 939 | dependencies = [ 940 | "quote", 941 | "wasm-bindgen-macro-support", 942 | ] 943 | 944 | [[package]] 945 | name = "wasm-bindgen-macro-support" 946 | version = "0.2.100" 947 | source = "registry+https://github.com/rust-lang/crates.io-index" 948 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 949 | dependencies = [ 950 | "proc-macro2", 951 | "quote", 952 | "syn 2.0.106", 953 | "wasm-bindgen-backend", 954 | "wasm-bindgen-shared", 955 | ] 956 | 957 | [[package]] 958 | name = "wasm-bindgen-shared" 959 | version = "0.2.100" 960 | source = "registry+https://github.com/rust-lang/crates.io-index" 961 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 962 | dependencies = [ 963 | "unicode-ident", 964 | ] 965 | 966 | [[package]] 967 | name = "wasm-encoder" 968 | version = "0.235.0" 969 | source = "registry+https://github.com/rust-lang/crates.io-index" 970 | checksum = "b3bc393c395cb621367ff02d854179882b9a351b4e0c93d1397e6090b53a5c2a" 971 | dependencies = [ 972 | "leb128fmt", 973 | "wasmparser", 974 | ] 975 | 976 | [[package]] 977 | name = "wasm-metadata" 978 | version = "0.235.0" 979 | source = "registry+https://github.com/rust-lang/crates.io-index" 980 | checksum = "b055604ba04189d54b8c0ab2c2fc98848f208e103882d5c0b984f045d5ea4d20" 981 | dependencies = [ 982 | "anyhow", 983 | "indexmap", 984 | "wasm-encoder", 985 | "wasmparser", 986 | ] 987 | 988 | [[package]] 989 | name = "wasmparser" 990 | version = "0.235.0" 991 | source = "registry+https://github.com/rust-lang/crates.io-index" 992 | checksum = "161296c618fa2d63f6ed5fffd1112937e803cb9ec71b32b01a76321555660917" 993 | dependencies = [ 994 | "bitflags", 995 | "hashbrown", 996 | "indexmap", 997 | "semver", 998 | ] 999 | 1000 | [[package]] 1001 | name = "windows-core" 1002 | version = "0.61.2" 1003 | source = "registry+https://github.com/rust-lang/crates.io-index" 1004 | checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" 1005 | dependencies = [ 1006 | "windows-implement", 1007 | "windows-interface", 1008 | "windows-link", 1009 | "windows-result", 1010 | "windows-strings", 1011 | ] 1012 | 1013 | [[package]] 1014 | name = "windows-implement" 1015 | version = "0.60.0" 1016 | source = "registry+https://github.com/rust-lang/crates.io-index" 1017 | checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" 1018 | dependencies = [ 1019 | "proc-macro2", 1020 | "quote", 1021 | "syn 2.0.106", 1022 | ] 1023 | 1024 | [[package]] 1025 | name = "windows-interface" 1026 | version = "0.59.1" 1027 | source = "registry+https://github.com/rust-lang/crates.io-index" 1028 | checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" 1029 | dependencies = [ 1030 | "proc-macro2", 1031 | "quote", 1032 | "syn 2.0.106", 1033 | ] 1034 | 1035 | [[package]] 1036 | name = "windows-link" 1037 | version = "0.1.3" 1038 | source = "registry+https://github.com/rust-lang/crates.io-index" 1039 | checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" 1040 | 1041 | [[package]] 1042 | name = "windows-result" 1043 | version = "0.3.4" 1044 | source = "registry+https://github.com/rust-lang/crates.io-index" 1045 | checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" 1046 | dependencies = [ 1047 | "windows-link", 1048 | ] 1049 | 1050 | [[package]] 1051 | name = "windows-strings" 1052 | version = "0.4.2" 1053 | source = "registry+https://github.com/rust-lang/crates.io-index" 1054 | checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" 1055 | dependencies = [ 1056 | "windows-link", 1057 | ] 1058 | 1059 | [[package]] 1060 | name = "wit-bindgen" 1061 | version = "0.43.0" 1062 | source = "registry+https://github.com/rust-lang/crates.io-index" 1063 | checksum = "9a18712ff1ec5bd09da500fe1e91dec11256b310da0ff33f8b4ec92b927cf0c6" 1064 | dependencies = [ 1065 | "wit-bindgen-rt 0.43.0", 1066 | "wit-bindgen-rust-macro", 1067 | ] 1068 | 1069 | [[package]] 1070 | name = "wit-bindgen" 1071 | version = "0.45.0" 1072 | source = "registry+https://github.com/rust-lang/crates.io-index" 1073 | checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" 1074 | 1075 | [[package]] 1076 | name = "wit-bindgen-core" 1077 | version = "0.43.0" 1078 | source = "registry+https://github.com/rust-lang/crates.io-index" 1079 | checksum = "2c53468e077362201de11999c85c07c36e12048a990a3e0d69da2bd61da355d0" 1080 | dependencies = [ 1081 | "anyhow", 1082 | "heck", 1083 | "wit-parser", 1084 | ] 1085 | 1086 | [[package]] 1087 | name = "wit-bindgen-rt" 1088 | version = "0.24.0" 1089 | source = "registry+https://github.com/rust-lang/crates.io-index" 1090 | checksum = "3b0780cf7046630ed70f689a098cd8d56c5c3b22f2a7379bbdb088879963ff96" 1091 | dependencies = [ 1092 | "bitflags", 1093 | ] 1094 | 1095 | [[package]] 1096 | name = "wit-bindgen-rt" 1097 | version = "0.43.0" 1098 | source = "registry+https://github.com/rust-lang/crates.io-index" 1099 | checksum = "9fd734226eac1fd7c450956964e3a9094c9cee65e9dafdf126feef8c0096db65" 1100 | dependencies = [ 1101 | "bitflags", 1102 | "futures", 1103 | "once_cell", 1104 | ] 1105 | 1106 | [[package]] 1107 | name = "wit-bindgen-rust" 1108 | version = "0.43.0" 1109 | source = "registry+https://github.com/rust-lang/crates.io-index" 1110 | checksum = "531ebfcec48e56473805285febdb450e270fa75b2dacb92816861d0473b4c15f" 1111 | dependencies = [ 1112 | "anyhow", 1113 | "heck", 1114 | "indexmap", 1115 | "prettyplease", 1116 | "syn 2.0.106", 1117 | "wasm-metadata", 1118 | "wit-bindgen-core", 1119 | "wit-component", 1120 | ] 1121 | 1122 | [[package]] 1123 | name = "wit-bindgen-rust-macro" 1124 | version = "0.43.0" 1125 | source = "registry+https://github.com/rust-lang/crates.io-index" 1126 | checksum = "7852bf8a9d1ea80884d26b864ddebd7b0c7636697c6ca10f4c6c93945e023966" 1127 | dependencies = [ 1128 | "anyhow", 1129 | "prettyplease", 1130 | "proc-macro2", 1131 | "quote", 1132 | "syn 2.0.106", 1133 | "wit-bindgen-core", 1134 | "wit-bindgen-rust", 1135 | ] 1136 | 1137 | [[package]] 1138 | name = "wit-component" 1139 | version = "0.235.0" 1140 | source = "registry+https://github.com/rust-lang/crates.io-index" 1141 | checksum = "64a57a11109cc553396f89f3a38a158a97d0b1adaec113bd73e0f64d30fb601f" 1142 | dependencies = [ 1143 | "anyhow", 1144 | "bitflags", 1145 | "indexmap", 1146 | "log", 1147 | "serde", 1148 | "serde_derive", 1149 | "serde_json", 1150 | "wasm-encoder", 1151 | "wasm-metadata", 1152 | "wasmparser", 1153 | "wit-parser", 1154 | ] 1155 | 1156 | [[package]] 1157 | name = "wit-parser" 1158 | version = "0.235.0" 1159 | source = "registry+https://github.com/rust-lang/crates.io-index" 1160 | checksum = "0a1f95a87d03a33e259af286b857a95911eb46236a0f726cbaec1227b3dfc67a" 1161 | dependencies = [ 1162 | "anyhow", 1163 | "id-arena", 1164 | "indexmap", 1165 | "log", 1166 | "semver", 1167 | "serde", 1168 | "serde_derive", 1169 | "serde_json", 1170 | "unicode-xid", 1171 | "wasmparser", 1172 | ] 1173 | 1174 | [[package]] 1175 | name = "zerocopy" 1176 | version = "0.8.26" 1177 | source = "registry+https://github.com/rust-lang/crates.io-index" 1178 | checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" 1179 | dependencies = [ 1180 | "zerocopy-derive", 1181 | ] 1182 | 1183 | [[package]] 1184 | name = "zerocopy-derive" 1185 | version = "0.8.26" 1186 | source = "registry+https://github.com/rust-lang/crates.io-index" 1187 | checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" 1188 | dependencies = [ 1189 | "proc-macro2", 1190 | "quote", 1191 | "syn 2.0.106", 1192 | ] 1193 | --------------------------------------------------------------------------------