use gts::GtsIdSegment;
use uuid::Uuid;
#[derive(Debug, Clone, PartialEq)]
pub struct GtsEntity<C = serde_json::Value> {
pub id: Uuid,
pub gts_id: String,
pub segments: Vec<GtsIdSegment>,
pub is_schema: bool,
pub content: C,
pub description: Option<String>,
}
pub type DynGtsEntity = GtsEntity<serde_json::Value>;
#[derive(Debug, Clone, PartialEq, Default)]
pub struct TypeSchema(pub serde_json::Value);
impl TypeSchema {
#[must_use]
pub fn new(value: serde_json::Value) -> Self {
Self(value)
}
#[must_use]
pub fn into_inner(self) -> serde_json::Value {
self.0
}
}
impl std::ops::Deref for TypeSchema {
type Target = serde_json::Value;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<serde_json::Value> for TypeSchema {
fn as_ref(&self) -> &serde_json::Value {
&self.0
}
}
impl From<serde_json::Value> for TypeSchema {
fn from(value: serde_json::Value) -> Self {
Self(value)
}
}
impl From<TypeSchema> for serde_json::Value {
fn from(schema: TypeSchema) -> Self {
schema.0
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct InstanceObject(pub serde_json::Value);
impl InstanceObject {
#[must_use]
pub fn new(value: serde_json::Value) -> Self {
Self(value)
}
#[must_use]
pub fn into_inner(self) -> serde_json::Value {
self.0
}
}
impl std::ops::Deref for InstanceObject {
type Target = serde_json::Value;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<serde_json::Value> for InstanceObject {
fn as_ref(&self) -> &serde_json::Value {
&self.0
}
}
impl From<serde_json::Value> for InstanceObject {
fn from(value: serde_json::Value) -> Self {
Self(value)
}
}
impl From<InstanceObject> for serde_json::Value {
fn from(instance: InstanceObject) -> Self {
instance.0
}
}
pub type GtsTypeEntity = GtsEntity<TypeSchema>;
pub type GtsInstanceEntity = GtsEntity<InstanceObject>;
#[derive(Debug, Clone)]
pub enum RegisterResult<C = serde_json::Value> {
Ok(GtsEntity<C>),
Err {
gts_id: Option<String>,
error: crate::TypesRegistryError,
},
}
impl<C> RegisterResult<C> {
#[must_use]
pub const fn is_ok(&self) -> bool {
matches!(self, Self::Ok(_))
}
#[must_use]
pub const fn is_err(&self) -> bool {
matches!(self, Self::Err { .. })
}
pub fn as_result(&self) -> Result<&GtsEntity<C>, &crate::TypesRegistryError> {
match self {
Self::Ok(entity) => Ok(entity),
Self::Err { error, .. } => Err(error),
}
}
pub fn into_result(self) -> Result<GtsEntity<C>, crate::TypesRegistryError> {
match self {
Self::Ok(entity) => Ok(entity),
Self::Err { error, .. } => Err(error),
}
}
#[must_use]
pub fn ok(self) -> Option<GtsEntity<C>> {
match self {
Self::Ok(entity) => Some(entity),
Self::Err { .. } => None,
}
}
#[must_use]
pub fn err(self) -> Option<crate::TypesRegistryError> {
match self {
Self::Ok(_) => None,
Self::Err { error, .. } => Some(error),
}
}
}
pub type DynRegisterResult = RegisterResult<serde_json::Value>;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct RegisterSummary {
pub succeeded: usize,
pub failed: usize,
}
impl RegisterSummary {
#[must_use]
pub fn from_results<C>(results: &[RegisterResult<C>]) -> Self {
let succeeded = results.iter().filter(|r| r.is_ok()).count();
let failed = results.len() - succeeded;
Self { succeeded, failed }
}
#[must_use]
pub const fn all_succeeded(&self) -> bool {
self.failed == 0
}
#[must_use]
pub const fn all_failed(&self) -> bool {
self.succeeded == 0
}
#[must_use]
pub const fn total(&self) -> usize {
self.succeeded + self.failed
}
}
impl<C> GtsEntity<C> {
#[must_use]
pub fn new(
id: Uuid,
gts_id: impl Into<String>,
segments: Vec<GtsIdSegment>,
is_schema: bool,
content: C,
description: Option<String>,
) -> Self {
Self {
id,
gts_id: gts_id.into(),
segments,
is_schema,
content,
description,
}
}
#[must_use]
pub const fn is_type(&self) -> bool {
self.is_schema
}
#[must_use]
pub const fn is_instance(&self) -> bool {
!self.is_schema
}
#[must_use]
pub fn primary_segment(&self) -> Option<&GtsIdSegment> {
self.segments.first()
}
#[must_use]
pub fn vendor(&self) -> Option<&str> {
self.primary_segment().map(|s| s.vendor.as_str())
}
#[must_use]
pub fn package(&self) -> Option<&str> {
self.primary_segment().map(|s| s.package.as_str())
}
#[must_use]
pub fn namespace(&self) -> Option<&str> {
self.primary_segment().map(|s| s.namespace.as_str())
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum SegmentMatchScope {
Primary,
#[default]
Any,
}
impl SegmentMatchScope {
#[must_use]
pub const fn is_primary(self) -> bool {
matches!(self, Self::Primary)
}
#[must_use]
pub const fn is_any(self) -> bool {
matches!(self, Self::Any)
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ListQuery {
pub pattern: Option<String>,
pub is_type: Option<bool>,
pub vendor: Option<String>,
pub package: Option<String>,
pub namespace: Option<String>,
pub segment_scope: SegmentMatchScope,
}
impl ListQuery {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_pattern(mut self, pattern: impl Into<String>) -> Self {
self.pattern = Some(pattern.into());
self
}
#[must_use]
pub const fn with_is_type(mut self, is_type: bool) -> Self {
self.is_type = Some(is_type);
self
}
#[must_use]
pub fn with_vendor(mut self, vendor: impl Into<String>) -> Self {
self.vendor = Some(vendor.into());
self
}
#[must_use]
pub fn with_package(mut self, package: impl Into<String>) -> Self {
self.package = Some(package.into());
self
}
#[must_use]
pub fn with_namespace(mut self, namespace: impl Into<String>) -> Self {
self.namespace = Some(namespace.into());
self
}
#[must_use]
pub const fn with_segment_scope(mut self, scope: SegmentMatchScope) -> Self {
self.segment_scope = scope;
self
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.pattern.is_none()
&& self.is_type.is_none()
&& self.vendor.is_none()
&& self.package.is_none()
&& self.namespace.is_none()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gts_id_segment_from_gts_rust() {
let segment = GtsIdSegment::new(0, 0, "acme.core.events.user_created.v1~").unwrap();
assert_eq!(segment.vendor, "acme");
assert_eq!(segment.package, "core");
assert_eq!(segment.namespace, "events");
assert_eq!(segment.type_name, "user_created");
assert_eq!(segment.ver_major, 1);
assert!(segment.is_type);
}
#[test]
fn test_gts_entity_accessors() {
let segment = GtsIdSegment::new(0, 0, "acme.core.events.user_created.v1~").unwrap();
let entity = GtsEntity::new(
Uuid::nil(),
"gts.acme.core.events.user_created.v1~",
vec![segment],
true, serde_json::json!({"type": "object"}),
Some("A user created event".to_owned()),
);
assert!(entity.is_type());
assert!(!entity.is_instance());
assert_eq!(entity.vendor(), Some("acme"));
assert_eq!(entity.package(), Some("core"));
assert_eq!(entity.namespace(), Some("events"));
let instance = GtsEntity::new(
Uuid::nil(),
"gts.acme.core.events.user_created.v1~acme.core.instances.instance1.v1",
vec![],
false, serde_json::json!({"data": "value"}),
None,
);
assert!(!instance.is_type());
assert!(instance.is_instance());
}
#[test]
fn test_list_query_builder() {
let query = ListQuery::new()
.with_pattern("gts.acme.*")
.with_is_type(true)
.with_vendor("acme")
.with_package("core")
.with_namespace("events");
assert_eq!(query.pattern, Some("gts.acme.*".to_owned()));
assert_eq!(query.is_type, Some(true));
assert_eq!(query.vendor, Some("acme".to_owned()));
assert_eq!(query.package, Some("core".to_owned()));
assert_eq!(query.namespace, Some("events".to_owned()));
assert_eq!(query.segment_scope, SegmentMatchScope::Any);
assert!(!query.is_empty());
}
#[test]
fn test_list_query_empty() {
let query = ListQuery::default();
assert!(query.is_empty());
assert_eq!(query.segment_scope, SegmentMatchScope::Any);
}
#[test]
fn test_segment_match_scope() {
assert!(SegmentMatchScope::Primary.is_primary());
assert!(!SegmentMatchScope::Primary.is_any());
assert!(SegmentMatchScope::Any.is_any());
assert!(!SegmentMatchScope::Any.is_primary());
assert_eq!(SegmentMatchScope::default(), SegmentMatchScope::Any);
}
#[test]
fn test_list_query_with_segment_scope() {
let query = ListQuery::new()
.with_vendor("acme")
.with_segment_scope(SegmentMatchScope::Any);
assert_eq!(query.vendor, Some("acme".to_owned()));
assert_eq!(query.segment_scope, SegmentMatchScope::Any);
}
}