diff --git a/.github/workflows/commit-code-generation.yaml b/.github/workflows/check-code-generation.yaml similarity index 52% rename from .github/workflows/commit-code-generation.yaml rename to .github/workflows/check-code-generation.yaml index 829d405..f0516d4 100644 --- a/.github/workflows/commit-code-generation.yaml +++ b/.github/workflows/check-code-generation.yaml @@ -16,7 +16,7 @@ # # SPDX-License-Identifier: Apache-2.0 -name: commit-codegen +name: check-codegen on: workflow_call: @@ -27,27 +27,12 @@ permissions: jobs: commit: - name: Commit and push generated code + name: Check generated code runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Download all workflow run artifacts uses: actions/download-artifact@v3 - - name: Commit the generated code + - name: Check the generated code run: | - git config user.name github-actions - git config user.email github-actions@github.com - git add ./python - git diff --staged --quiet || git commit -s -m "[Python]: Add generated code" - mv ./rust-dist/astarteplatform.msghub.rs ./rust/astarte-message-hub-proto/src/astarteplatform.msghub.rs - git add ./rust/astarte-message-hub-proto/src/astarteplatform.msghub.rs - git diff --staged --quiet || git commit -s -m "[Rust]: Add generated code" - - name: Create pull request - uses: peter-evans/create-pull-request@v5 - with: - title: Update generated code - base: master - labels: automated-pr - branch: update-code-gen - delete-branch: true - signoff: true + git --no-pager diff --no-index -- ./rust-dist/astarteplatform.msghub.rs ./rust/astarte-message-hub-proto/src/astarteplatform.msghub.rs diff --git a/.github/workflows/code-generation.yaml b/.github/workflows/code-generation.yaml index aa485a3..d8fb234 100644 --- a/.github/workflows/code-generation.yaml +++ b/.github/workflows/code-generation.yaml @@ -39,12 +39,9 @@ jobs: rust-code-generation: needs: [ reuse ] uses: ./.github/workflows/rust-code-generation.yaml + check-codegen: + needs: [ python-code-generation, rust-code-generation ] + uses: ./.github/workflows/check-code-generation.yaml rust-code-check: - needs: [ rust-code-generation ] + needs: [ check-codegen ] uses: ./.github/workflows/rust-code-check.yaml - commit-codegen: - # if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' - needs: [ python-code-generation, rust-code-check ] - permissions: - contents: write - uses: ./.github/workflows/commit-code-generation.yaml diff --git a/.github/workflows/rust-code-check.yaml b/.github/workflows/rust-code-check.yaml index d3a53ca..4580a22 100644 --- a/.github/workflows/rust-code-check.yaml +++ b/.github/workflows/rust-code-check.yaml @@ -52,10 +52,6 @@ jobs: toolchain: [ stable ] steps: - uses: actions/checkout@v4 - - uses: actions/download-artifact@v3 - with: - name: rust-dist - path: rust/astarte-message-hub-proto/src - name: Install ${{ matrix.toolchain }} uses: dtolnay/rust-toolchain@master with: @@ -74,10 +70,6 @@ jobs: - uses: actions/checkout@v4 - name: Install nightly uses: dtolnay/rust-toolchain@nightly - - uses: actions/download-artifact@v3 - with: - name: rust-dist - path: rust/astarte-message-hub-proto/src - name: cargo doc run: cargo doc --no-deps --all-features env: @@ -92,10 +84,6 @@ jobs: uses: dtolnay/rust-toolchain@stable - name: cargo install cargo-hack uses: taiki-e/install-action@cargo-hack - - uses: actions/download-artifact@v3 - with: - name: rust-dist - path: rust/astarte-message-hub-proto/src # intentionally no target specifier; see https://github.com/jonhoo/rust-ci-conf/pull/4 - name: cargo hack run: cargo hack --feature-powerset check @@ -114,9 +102,5 @@ jobs: uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.msrv }} - - uses: actions/download-artifact@v3 - with: - name: rust-dist - path: rust/astarte-message-hub-proto/src - name: cargo +${{ matrix.msrv }} check run: cargo check --all-features --package astarte-message-hub-proto diff --git a/.github/workflows/rust-publish.yaml b/.github/workflows/rust-publish.yaml index c79431b..e9e04e3 100644 --- a/.github/workflows/rust-publish.yaml +++ b/.github/workflows/rust-publish.yaml @@ -31,7 +31,7 @@ jobs: - name: Install toolchain uses: dtolnay/rust-toolchain@master with: - toolchain: 1.59 + toolchain: stable - name: Publish astarte-message-hub-proto run: cargo publish --token ${CRATES_TOKEN} --dry-run env: diff --git a/.reuse/dep5 b/.reuse/dep5 index ac0020f..83e731b 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -5,3 +5,7 @@ Source: https://github.com/astarte-platform/astarte-message-hub-proto Files: python/astarteplatform/msghub/* Copyright: SECO Mind Srl License: CC0-1.0 + +Files: rust/astarte-message-hub-proto/src/astarteplatform.msghub.rs +Copyright: SECO Mind Srl +License: Apache-2.0 diff --git a/rust/astarte-message-hub-proto/src/astarteplatform.msghub.rs b/rust/astarte-message-hub-proto/src/astarteplatform.msghub.rs new file mode 100644 index 0000000..7b75dfc --- /dev/null +++ b/rust/astarte-message-hub-proto/src/astarteplatform.msghub.rs @@ -0,0 +1,558 @@ +/// An array of doubles for transmission with protobuf. +/// To be used nested inside an `AstarteDataTypeIndividual`. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AstarteDoubleArray { + #[prost(double, repeated, tag = "1")] + pub values: ::prost::alloc::vec::Vec, +} +/// An array of int32 for transmission with protobuf. +/// To be used nested inside an `AstarteDataTypeIndividual`. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AstarteIntegerArray { + #[prost(int32, repeated, tag = "1")] + pub values: ::prost::alloc::vec::Vec, +} +/// An array of booleans for transmission with protobuf. +/// To be used nested inside an `AstarteDataTypeIndividual`. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AstarteBooleanArray { + #[prost(bool, repeated, tag = "1")] + pub values: ::prost::alloc::vec::Vec, +} +/// An array of int64 for transmission with protobuf. +/// To be used nested inside an `AstarteDataTypeIndividual`. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AstarteLongIntegerArray { + #[prost(int64, repeated, tag = "1")] + pub values: ::prost::alloc::vec::Vec, +} +/// An array of strings for transmission with protobuf. +/// To be used nested inside an `AstarteDataTypeIndividual`. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AstarteStringArray { + #[prost(string, repeated, tag = "1")] + pub values: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +/// An array of bytes for transmission with protobuf. +/// To be used nested inside an `AstarteDataTypeIndividual`. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AstarteBinaryBlobArray { + #[prost(bytes = "vec", repeated, tag = "1")] + pub values: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, +} +/// An array of timestamps for transmission with protobuf. +/// To be used nested inside an `AstarteDataTypeIndividual`. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AstarteDateTimeArray { + #[prost(message, repeated, tag = "1")] + pub values: ::prost::alloc::vec::Vec<::pbjson_types::Timestamp>, +} +/// An aggregated object data type for transmission with protobuf. +/// To be used nested inside an `AstarteDataType`. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AstarteDataTypeObject { + #[prost(map = "string, message", tag = "1")] + pub object_data: ::std::collections::HashMap< + ::prost::alloc::string::String, + AstarteDataTypeIndividual, + >, +} +/// An individual data type for transmission with protobuf. +/// To be used nested inside an `AstarteDataType`. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AstarteDataTypeIndividual { + #[prost( + oneof = "astarte_data_type_individual::IndividualData", + tags = "1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14" + )] + pub individual_data: ::core::option::Option< + astarte_data_type_individual::IndividualData, + >, +} +/// Nested message and enum types in `AstarteDataTypeIndividual`. +pub mod astarte_data_type_individual { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum IndividualData { + #[prost(double, tag = "1")] + AstarteDouble(f64), + #[prost(int32, tag = "2")] + AstarteInteger(i32), + #[prost(bool, tag = "3")] + AstarteBoolean(bool), + #[prost(int64, tag = "4")] + AstarteLongInteger(i64), + #[prost(string, tag = "5")] + AstarteString(::prost::alloc::string::String), + #[prost(bytes, tag = "6")] + AstarteBinaryBlob(::prost::alloc::vec::Vec), + #[prost(message, tag = "7")] + AstarteDateTime(::pbjson_types::Timestamp), + #[prost(message, tag = "8")] + AstarteDoubleArray(super::AstarteDoubleArray), + #[prost(message, tag = "9")] + AstarteIntegerArray(super::AstarteIntegerArray), + #[prost(message, tag = "10")] + AstarteBooleanArray(super::AstarteBooleanArray), + #[prost(message, tag = "11")] + AstarteLongIntegerArray(super::AstarteLongIntegerArray), + #[prost(message, tag = "12")] + AstarteStringArray(super::AstarteStringArray), + #[prost(message, tag = "13")] + AstarteBinaryBlobArray(super::AstarteBinaryBlobArray), + #[prost(message, tag = "14")] + AstarteDateTimeArray(super::AstarteDateTimeArray), + } +} +/// A generic data type to be used nested in an `AstarteMessage`. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AstarteDataType { + /// Content for an `AstarteDataType`. + #[prost(oneof = "astarte_data_type::Data", tags = "1, 2")] + pub data: ::core::option::Option, +} +/// Nested message and enum types in `AstarteDataType`. +pub mod astarte_data_type { + /// Content for an `AstarteDataType`. + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Data { + /// An individual data type. + #[prost(message, tag = "1")] + AstarteIndividual(super::AstarteDataTypeIndividual), + /// An aggregated data type. + #[prost(message, tag = "2")] + AstarteObject(super::AstarteDataTypeObject), + } +} +/// Astarte message to be used when sending data to Astarte. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AstarteMessage { + /// Name of the interface to send data on. + #[prost(string, tag = "1")] + pub interface_name: ::prost::alloc::string::String, + /// Endpoint to send the data on. + #[prost(string, tag = "2")] + pub path: ::prost::alloc::string::String, + /// Explicit timestamp for the message transmission. + #[prost(message, optional, tag = "5")] + pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>, + /// Content of the message. + #[prost(oneof = "astarte_message::Payload", tags = "3, 4")] + pub payload: ::core::option::Option, +} +/// Nested message and enum types in `AstarteMessage`. +pub mod astarte_message { + /// Content of the message. + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Payload { + /// Effective payload. + #[prost(message, tag = "3")] + AstarteData(super::AstarteDataType), + /// Null payload. + #[prost(message, tag = "4")] + AstarteUnset(super::AstarteUnset), + } +} +/// Null payload for an `AstarteMessage`. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AstarteUnset {} +/// This message defines a node to be attached/detached to the Astarte message hub. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Node { + /// The node identifier. + #[prost(string, tag = "1")] + pub uuid: ::prost::alloc::string::String, + /// Array of byte arrays representing all .json interface files of the node. + #[prost(bytes = "vec", repeated, tag = "2")] + pub interface_jsons: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, +} +/// Generated client implementations. +pub mod message_hub_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + #[derive(Debug, Clone)] + pub struct MessageHubClient { + inner: tonic::client::Grpc, + } + impl MessageHubClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: std::convert::TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl MessageHubClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + Send + 'static, + ::Error: Into + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> MessageHubClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + Send + Sync, + { + MessageHubClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// This function should be used to attach a node to an instance of the Astarte message hub. + /// Returns a data stream from the Astarte message hub. + pub async fn attach( + &mut self, + request: impl tonic::IntoRequest, + ) -> Result< + tonic::Response>, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/astarteplatform.msghub.MessageHub/Attach", + ); + self.inner.server_streaming(request.into_request(), path, codec).await + } + /// This function should be used to send an `AstarteMessage` to Astarte. + pub async fn send( + &mut self, + request: impl tonic::IntoRequest, + ) -> Result, tonic::Status> { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/astarteplatform.msghub.MessageHub/Send", + ); + self.inner.unary(request.into_request(), path, codec).await + } + /// This function should be used to detach a node from an instance of the Astarte message hub. + pub async fn detach( + &mut self, + request: impl tonic::IntoRequest, + ) -> Result, tonic::Status> { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/astarteplatform.msghub.MessageHub/Detach", + ); + self.inner.unary(request.into_request(), path, codec).await + } + } +} +/// Generated server implementations. +pub mod message_hub_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + ///Generated trait containing gRPC methods that should be implemented for use with MessageHubServer. + #[async_trait] + pub trait MessageHub: Send + Sync + 'static { + ///Server streaming response type for the Attach method. + type AttachStream: futures_core::Stream< + Item = Result, + > + + Send + + 'static; + /// This function should be used to attach a node to an instance of the Astarte message hub. + /// Returns a data stream from the Astarte message hub. + async fn attach( + &self, + request: tonic::Request, + ) -> Result, tonic::Status>; + /// This function should be used to send an `AstarteMessage` to Astarte. + async fn send( + &self, + request: tonic::Request, + ) -> Result, tonic::Status>; + /// This function should be used to detach a node from an instance of the Astarte message hub. + async fn detach( + &self, + request: tonic::Request, + ) -> Result, tonic::Status>; + } + #[derive(Debug)] + pub struct MessageHubServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + } + struct _Inner(Arc); + impl MessageHubServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + } + impl tonic::codegen::Service> for MessageHubServer + where + T: MessageHub, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/astarteplatform.msghub.MessageHub/Attach" => { + #[allow(non_camel_case_types)] + struct AttachSvc(pub Arc); + impl< + T: MessageHub, + > tonic::server::ServerStreamingService + for AttachSvc { + type Response = super::AstarteMessage; + type ResponseStream = T::AttachStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = self.0.clone(); + let fut = async move { (*inner).attach(request).await }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = AttachSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/astarteplatform.msghub.MessageHub/Send" => { + #[allow(non_camel_case_types)] + struct SendSvc(pub Arc); + impl< + T: MessageHub, + > tonic::server::UnaryService for SendSvc { + type Response = ::pbjson_types::Empty; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = self.0.clone(); + let fut = async move { (*inner).send(request).await }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = SendSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/astarteplatform.msghub.MessageHub/Detach" => { + #[allow(non_camel_case_types)] + struct DetachSvc(pub Arc); + impl tonic::server::UnaryService + for DetachSvc { + type Response = ::pbjson_types::Empty; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = self.0.clone(); + let fut = async move { (*inner).detach(request).await }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = DetachSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for MessageHubServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(self.0.clone()) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService for MessageHubServer { + const NAME: &'static str = "astarteplatform.msghub.MessageHub"; + } +}