use std::sync::Arc;
use hyphae::SwitchMapExt;
use crate::prelude::*;
pub use graph_edge::{BenchGraphEdge, BenchGraphEdgeId, EnsureBenchGraphEdge};
pub use graph_edge_demand::{BenchDemandGraphEdge, BenchDemandGraphEdgeId};
pub use graph_edge_forward::{BenchForwardGraphEdge, BenchForwardGraphEdgeId};
pub use graph_edge_undirected::{BenchUndirectedGraphEdge, BenchUndirectedGraphEdgeId};
pub use graph_node::{BenchGraphNode, BenchGraphNodeId};
mod graph_node {
use crate::prelude::*;
#[myko_item]
pub struct BenchGraphNode {
pub ordinal: i64,
}
}
mod graph_edge {
use super::{BenchGraphNode, BenchGraphNodeId};
use crate::prelude::*;
#[myko_item]
pub struct BenchGraphEdge {
pub from_id: BenchGraphNodeId,
pub to_id: BenchGraphNodeId,
}
#[myko_edge]
impl GraphEdge for BenchGraphEdge {
type Ends = Directed<ConcreteEndpoint<BenchGraphNode>, ConcreteEndpoint<BenchGraphNode>>;
fn ends(&self) -> (BenchGraphNodeId, BenchGraphNodeId) {
(self.from_id.clone(), self.to_id.clone())
}
const ADJACENCY: AdjacencyPolicy = AdjacencyPolicy::Eager;
const PAIR_POLICY: PairPolicy = PairPolicy::Unique;
}
}
mod graph_edge_forward {
use super::{BenchGraphNode, BenchGraphNodeId};
use crate::prelude::*;
#[myko_item]
pub struct BenchForwardGraphEdge {
pub from_id: BenchGraphNodeId,
pub to_id: BenchGraphNodeId,
}
#[myko_edge]
impl GraphEdge for BenchForwardGraphEdge {
type Ends = Directed<ConcreteEndpoint<BenchGraphNode>, ConcreteEndpoint<BenchGraphNode>>;
fn ends(&self) -> (BenchGraphNodeId, BenchGraphNodeId) {
(self.from_id.clone(), self.to_id.clone())
}
const A_ADJACENCY: AdjacencyPolicy = AdjacencyPolicy::Eager;
const PAIR_POLICY: PairPolicy = PairPolicy::Unique;
}
}
mod graph_edge_demand {
use super::{BenchGraphNode, BenchGraphNodeId};
use crate::prelude::*;
#[myko_item]
pub struct BenchDemandGraphEdge {
pub from_id: BenchGraphNodeId,
pub to_id: BenchGraphNodeId,
}
#[myko_edge]
impl GraphEdge for BenchDemandGraphEdge {
type Ends = Directed<ConcreteEndpoint<BenchGraphNode>, ConcreteEndpoint<BenchGraphNode>>;
fn ends(&self) -> (BenchGraphNodeId, BenchGraphNodeId) {
(self.from_id.clone(), self.to_id.clone())
}
}
}
mod graph_edge_undirected {
use super::{BenchGraphNode, BenchGraphNodeId};
use crate::prelude::*;
#[myko_item]
pub struct BenchUndirectedGraphEdge {
pub a_id: BenchGraphNodeId,
pub b_id: BenchGraphNodeId,
}
#[myko_edge]
impl GraphEdge for BenchUndirectedGraphEdge {
type Ends = Undirected<ConcreteEndpoint<BenchGraphNode>, ConcreteEndpoint<BenchGraphNode>>;
fn ends(&self) -> (BenchGraphNodeId, BenchGraphNodeId) {
(self.a_id.clone(), self.b_id.clone())
}
const ADJACENCY: AdjacencyPolicy = AdjacencyPolicy::Eager;
}
}
#[myko_item]
pub struct BenchItem {
#[searchable]
pub name: String,
#[searchable]
pub category: String,
pub value: i64,
}
pub use tree::BenchTreeItem;
mod tree {
use std::sync::Arc;
use crate::prelude::*;
#[myko_item]
pub struct BenchTreeItem {
pub name: String,
pub parent_id: Option<Arc<str>>,
pub depth: i64,
}
}
pub use compound_a::{BenchParentA, BenchParentAId};
mod compound_a {
use crate::prelude::*;
#[myko_item]
pub struct BenchParentA {
pub name: String,
}
}
pub use compound_b::{BenchParentB, BenchParentBId};
mod compound_b {
use crate::prelude::*;
#[myko_item]
pub struct BenchParentB {
pub name: String,
}
}
pub use compound_child::{
BenchCompoundChild, BenchCompoundChildQuery, GetBenchCompoundChildsByQuery,
};
mod compound_child {
use super::{BenchParentA, BenchParentAId, BenchParentB, BenchParentBId};
use crate::prelude::*;
#[myko_item]
pub struct BenchCompoundChild {
#[belongs_to(BenchParentA)]
pub parent_a_id: BenchParentAId,
#[belongs_to(BenchParentB)]
pub parent_b_id: BenchParentBId,
pub value: i64,
}
}
pub use snapshot::BenchSnapshot;
mod snapshot {
use crate::prelude::*;
#[myko_item]
pub struct BenchSnapshot {
pub payload: serde_json::Value,
}
}
pub use containers::BenchContainerFields;
mod containers {
use std::collections::HashMap;
use crate::prelude::*;
#[myko_item]
pub struct BenchContainerFields {
pub tags: Vec<String>,
pub metadata: HashMap<String, i64>,
pub coordinates: (i64, i64),
}
}
#[cfg(feature = "ordered-float")]
pub use keyframe::BenchKeyframe;
#[cfg(feature = "ordered-float")]
mod keyframe {
use ordered_float::OrderedFloat;
use crate::prelude::*;
#[myko_item]
pub struct BenchKeyframe {
pub position: OrderedFloat<f64>,
}
}
pub use subtypes::{BenchSubtypeFields, BenchSubtypeOrdered, BenchSubtypeUnordered};
mod subtypes {
use crate::prelude::*;
#[myko_subtype(derive(Eq, Ord, PartialOrd))]
pub struct BenchSubtypeOrdered {
pub label: String,
}
#[myko_subtype(derive(Eq))]
pub struct BenchSubtypeUnordered {
pub note: String,
}
#[myko_item]
pub struct BenchSubtypeFields {
pub ordered: BenchSubtypeOrdered,
pub unordered: BenchSubtypeUnordered,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::query::{EqFilter, Filter, Filterable, Unfilterable};
#[test]
fn eq_and_ord_subtype_gets_eq_filter() {
let filter: <BenchSubtypeOrdered as Filterable>::Filter =
EqFilter::Eq(BenchSubtypeOrdered {
label: "a".to_string(),
});
assert!(filter.matches(&BenchSubtypeOrdered {
label: "a".to_string(),
}));
assert!(!filter.matches(&BenchSubtypeOrdered {
label: "b".to_string(),
}));
}
#[test]
fn default_subtype_falls_back_to_unfilterable() {
fn assert_unfilterable<T>()
where
T: Filterable<Filter = Unfilterable>,
{
}
assert_unfilterable::<BenchSubtypeUnordered>();
}
}
}
pub use manual_wire::BenchManualWireValue;
mod manual_wire {
use crate::prelude::*;
#[myko_subtype(derive(Default, Eq), manual(serde), export(as = "unknown"))]
pub struct BenchManualWireValue {
pub raw: String,
}
impl serde::Serialize for BenchManualWireValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.raw)
}
}
impl<'de> serde::Deserialize<'de> for BenchManualWireValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(Self {
raw: String::deserialize(deserializer)?,
})
}
}
#[myko_item]
pub struct BenchManualWireHolder {
pub value: BenchManualWireValue,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::query::{Filterable, Unfilterable};
#[test]
fn manual_wire_value_serializes_as_a_plain_string() {
let value = BenchManualWireValue {
raw: "hello".to_string(),
};
let json = serde_json::to_value(&value);
assert!(json.is_ok(), "serialize manual wire value");
let Ok(json) = json else {
return;
};
assert_eq!(json, serde_json::Value::String("hello".to_string()));
let round_tripped = serde_json::from_value::<BenchManualWireValue>(json);
assert!(round_tripped.is_ok(), "deserialize manual wire value");
let Ok(round_tripped) = round_tripped else {
return;
};
assert_eq!(round_tripped.raw, "hello");
}
#[test]
fn manual_wire_value_still_gets_the_filterable_auto_impl() {
fn assert_unfilterable<T>()
where
T: Filterable<Filter = Unfilterable>,
{
}
assert_unfilterable::<BenchManualWireValue>();
}
}
}
#[myko_query(BenchItem)]
pub struct GetBenchItemsByCategory {
pub category: String,
}
impl QueryHandler for GetBenchItemsByCategory {
fn test_entity(ctx: QueryTestContext<Self>) -> bool {
ctx.item.category == ctx.query.category.as_str()
}
}
#[myko_report(Vec<String>)]
pub struct SwitchMapReport {
pub category: String,
}
impl ReportHandler for SwitchMapReport {
type Output = Vec<String>;
fn compute(&self, ctx: ReportContext) -> impl Materialize<Arc<Self::Output>, Definite> {
let category = self.category.clone();
let items = ctx
.query_map(GetBenchItemsByQuery(BenchItemQuery {
category: Some(StringFilter::Eq(category.into())),
..Default::default()
}))
.items()
.materialize();
items.switch_map(move |items| {
if items.is_empty() {
return Cell::new(Arc::new(Vec::<String>::new())).lock();
}
let ids: Vec<BenchItemId> = items.iter().map(|item| item.id.clone()).collect();
ctx.query_map(GetBenchItemsByIds { ids })
.items()
.map(|items| {
Arc::new(
items
.iter()
.map(|item| item.name.clone())
.collect::<Vec<_>>(),
)
})
.materialize()
})
}
}
#[cfg(test)]
mod typed_search_tests {
use super::*;
use crate::search::typed::{Score, SearchIndex, SearchOptions};
fn item(id: &str, name: &str, category: &str) -> BenchItem {
BenchItem {
id: id.into(),
name: name.to_string(),
category: category.to_string(),
value: 0,
}
}
#[test]
fn macro_generated_impl_indexes_and_finds() {
let mut index = SearchIndex::<BenchItem>::new();
index.insert(&item("1", "audio mixer", "hardware"));
index.insert(&item("2", "video camera", "hardware"));
index.insert(&item("3", "lighting fixture", "props"));
let mixer = index.search("mixer", SearchOptions::default());
assert_eq!(mixer.len(), 1);
assert_eq!(mixer.first().map(|hit| hit.id.0.as_ref()), Some("1"));
assert_eq!(mixer.first().map(|hit| hit.score), Some(Score::Exact));
let hardware = index.search("hardware", SearchOptions::default());
assert_eq!(hardware.len(), 2);
}
#[test]
fn macro_generated_field_names_match_searchable_attrs() {
use crate::search::typed::Searchable;
assert_eq!(BenchItem::searchable_field_names(), &["name", "category"]);
}
#[test]
fn matched_field_resolves_against_macro_generated_order() {
let mut index = SearchIndex::<BenchItem>::new();
index.insert(&item("1", "alpha", "beta"));
let name_hits = index.search("alpha", SearchOptions::default());
assert!(!name_hits.is_empty(), "expected name hit");
let Some(name_hit) = name_hits.first() else {
return;
};
assert_eq!(name_hit.matched_field, 0, "alpha is the name field");
let category_hits = index.search("beta", SearchOptions::default());
assert!(!category_hits.is_empty(), "expected category hit");
let Some(cat_hit) = category_hits.first() else {
return;
};
assert_eq!(cat_hit.matched_field, 1, "beta is the category field");
}
#[test]
fn build_typed_registry_picks_up_macro_emitted_registration() {
let registry = crate::search::build_typed_registry();
assert!(
registry.entity_types().any(|t| t == "BenchItem"),
"BenchItem should be registered via inventory; got: {:?}",
registry.entity_types().collect::<Vec<_>>()
);
registry.insert(&item("1", "audio mixer", "hardware"));
let hits = registry.search("BenchItem", "mixer", SearchOptions::default());
assert_eq!(hits.len(), 1);
assert_eq!(hits.first().map(|hit| hit.id.as_ref()), Some("1"));
}
#[test]
fn macro_generated_typed_search_report_exists() {
let report = SearchBenchItem {
query: "audio".to_string(),
limit: 25,
};
assert_eq!(report.query, "audio");
assert_eq!(report.limit, 25);
let result = SearchBenchItemResult {
ids: vec![BenchItemId::from(std::sync::Arc::<str>::from("1"))],
};
assert_eq!(result.ids.len(), 1);
}
}