| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| use std::sync::Arc; |
| |
| use iggy::prelude::Partitioning as RustPartitioning; |
| use pyo3::{exceptions::PyValueError, prelude::*, types::PyBytes}; |
| use pyo3_stub_gen::{ |
| derive::{gen_stub_pyclass, gen_stub_pymethods}, |
| impl_stub_type, |
| }; |
| |
| /// Defines how a batch of messages is assigned to a topic partition. |
| #[derive(Clone)] |
| #[pyclass(from_py_object)] |
| #[gen_stub_pyclass] |
| pub struct Partitioning { |
| pub(crate) inner: Arc<RustPartitioning>, |
| } |
| |
| #[gen_stub_pymethods] |
| #[pymethods] |
| impl Partitioning { |
| /// Routes the batch to one partition selected by round-robin. |
| #[staticmethod] |
| pub fn balanced() -> Self { |
| Self { |
| inner: Arc::new(RustPartitioning::balanced()), |
| } |
| } |
| |
| /// Routes the batch to the specified partition. |
| /// |
| /// `partition_id` must be between 0 and `2**32 - 1`. The topic must contain |
| /// that partition when the batch is sent. |
| /// |
| /// Raises: |
| /// TypeError: If `partition_id` is not an integer. |
| /// OverflowError: If `partition_id` is outside the supported unsigned |
| /// 32-bit range. |
| #[staticmethod] |
| pub fn partition_id(partition_id: u32) -> Self { |
| Self { |
| inner: Arc::new(RustPartitioning::partition_id(partition_id)), |
| } |
| } |
| |
| /// Routes the batch to one partition selected by hashing `key`. |
| /// |
| /// `key` may be `str` or `bytes`. Strings are encoded as UTF-8; the encoded |
| /// key must contain between 1 and 255 bytes. |
| /// |
| /// Raises: |
| /// ValueError: If the encoded key is empty or exceeds 255 bytes. |
| /// TypeError: If `key` is not `str` or `bytes`. |
| #[staticmethod] |
| pub fn messages_key(py: Python<'_>, key: PyMessagesKey) -> PyResult<Self> { |
| let key = match key { |
| PyMessagesKey::String(key) => key.into_bytes(), |
| PyMessagesKey::Bytes(key) => key.extract::<Vec<u8>>(py)?, |
| }; |
| let inner = RustPartitioning::messages_key(&key) |
| .map_err(|error| PyValueError::new_err(error.to_string()))?; |
| Ok(Self { |
| inner: Arc::new(inner), |
| }) |
| } |
| } |
| |
| #[derive(FromPyObject)] |
| pub enum PyMessagesKey { |
| #[pyo3(transparent, annotation = "str")] |
| String(String), |
| #[pyo3(transparent, annotation = "bytes")] |
| Bytes(Py<PyBytes>), |
| } |
| impl_stub_type!(PyMessagesKey = String | PyBytes); |
| |
| #[derive(FromPyObject)] |
| pub(crate) enum PyPartitioning { |
| #[pyo3(transparent, annotation = "Partitioning")] |
| Strategy(Partitioning), |
| #[pyo3(transparent, annotation = "int")] |
| PartitionId(u32), |
| } |
| impl_stub_type!(PyPartitioning = Partitioning | isize); |
| |
| impl From<PyPartitioning> for RustPartitioning { |
| fn from(partitioning: PyPartitioning) -> Self { |
| match partitioning { |
| PyPartitioning::Strategy(partitioning) => partitioning.inner.as_ref().clone(), |
| PyPartitioning::PartitionId(partition_id) => Self::partition_id(partition_id), |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| #[test] |
| fn clone_shares_the_rust_partitioning() { |
| let partitioning = Partitioning::balanced(); |
| let cloned = partitioning.clone(); |
| |
| assert!(Arc::ptr_eq(&partitioning.inner, &cloned.inner)); |
| } |
| } |