blob: bb345cf5116c3ea4122e83b649e1704bc56a0df6 [file]
// 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));
}
}