lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

commit 307a428e6005a7fd6ba1f351136097bff694ad9e
parent 33e813eeb2f969227deb875a94833c2361febaed
Author: triesap <tyson@radroots.org>
Date:   Fri, 26 Dec 2025 03:15:23 +0000

tangle: add rel-aware FindOne args and custom Nostr event helpers


- Add custom event build/send and fetch_events helpers to NostrClientManager
- Refactor find_one/delete paths to dispatch on On vs Rel args and share helpers
- Generate ts-rs bindings for FindOne union args and empty FindManyRel markers
- Preallocate relation query param vectors and normalize event stream handling

Diffstat:
MAGENTS.md | 1+
Anet-core/src/nostr_client/events/custom.rs | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mnet-core/src/nostr_client/manager.rs | 8++------
Mnet-core/src/nostr_client/mod.rs | 1+
Mtangle-schema/bindings/ts/src/types.ts | 48+++++++++++++++++++++++++++++++++++++++++-------
Mtangle-schema/src/models/farm.rs | 31+++++++++++++++++++++++++++----
Mtangle-schema/src/models/location_gcs.rs | 22+++++++++++++++-------
Mtangle-schema/src/models/log_error.rs | 31+++++++++++++++++++++++++++----
Mtangle-schema/src/models/media_image.rs | 24++++++++++++++++++++----
Mtangle-schema/src/models/nostr_profile.rs | 24++++++++++++++++++++----
Mtangle-schema/src/models/nostr_relay.rs | 24++++++++++++++++++++----
Mtangle-schema/src/models/trade_product.rs | 31+++++++++++++++++++++++++++----
Mtangle-sql/migrations/0000_init.up.sql | 3++-
Mtangle-sql/src/models/farm.rs | 78+++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Mtangle-sql/src/models/farm_location.rs | 4++--
Mtangle-sql/src/models/location_gcs.rs | 53++++++++++++++++++++++++-----------------------------
Mtangle-sql/src/models/log_error.rs | 78+++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Mtangle-sql/src/models/media_image.rs | 103++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mtangle-sql/src/models/nostr_profile.rs | 103++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mtangle-sql/src/models/nostr_profile_relay.rs | 4++--
Mtangle-sql/src/models/nostr_relay.rs | 103++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mtangle-sql/src/models/trade_product.rs | 78+++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Mtangle-sql/src/models/trade_product_location.rs | 4++--
Mtangle-sql/src/models/trade_product_media.rs | 4++--
24 files changed, 652 insertions(+), 268 deletions(-)

diff --git a/AGENTS.md b/AGENTS.md @@ -32,6 +32,7 @@ - Safety: avoid unsafe; prefer safe, explicit APIs. Add #![forbid(unsafe_code)] on new crates/modules. - Public API: keep Radroots* prefix; avoid hidden panics; return Result/Option for fallible ops; use precise error enums (thiserror where appropriate). - Features: keep serde/typeshare/ts-rs derives behind existing feature gates and in the current style; ensure feature combinations compile (no_std, std, wasm). +- Events modules: in `crates/events`, each `.rs` file must map to a single Nostr kind (or a single NIP-defined kind range), and a kind must not be defined in multiple modules. - Generated outputs: treat */bindings/ts/src/types.ts as generated; do not hand-edit. - Performance: borrow over clone, avoid intermediate allocations, preallocate when sizes are known, and prefer iterators over indexing loops. - DRY: consolidate shared logic into core/types/events-codec or dedicated helpers. diff --git a/net-core/src/nostr_client/events/custom.rs b/net-core/src/nostr_client/events/custom.rs @@ -0,0 +1,60 @@ +#![forbid(unsafe_code)] + +use crate::error::{NetError, Result}; +use radroots_nostr::prelude::{ + radroots_nostr_build_event, + radroots_nostr_send_event, + RadrootsNostrEvent, + RadrootsNostrFilter, +}; + +use crate::nostr_client::manager::NostrClientManager; + +impl NostrClientManager { + pub async fn send_custom_event( + &self, + kind: u32, + content: String, + tags: Vec<Vec<String>>, + ) -> Result<String> { + let builder = radroots_nostr_build_event(kind, content, tags) + .map_err(|e| NetError::Msg(e.to_string()))?; + let out = radroots_nostr_send_event(&self.inner.client, builder) + .await + .map_err(|e| NetError::Msg(e.to_string()))?; + Ok(out.val.to_string()) + } + + pub fn send_custom_event_blocking( + &self, + kind: u32, + content: String, + tags: Vec<Vec<String>>, + ) -> Result<String> { + let rt = self.inner.rt.clone(); + let this = self.clone(); + rt.block_on(async move { this.send_custom_event(kind, content, tags).await }) + } + + pub async fn fetch_events( + &self, + filter: RadrootsNostrFilter, + timeout: core::time::Duration, + ) -> Result<Vec<RadrootsNostrEvent>> { + self.inner + .client + .fetch_events(filter, timeout) + .await + .map_err(|e| NetError::Msg(e.to_string())) + } + + pub fn fetch_events_blocking( + &self, + filter: RadrootsNostrFilter, + timeout: core::time::Duration, + ) -> Result<Vec<RadrootsNostrEvent>> { + let rt = self.inner.rt.clone(); + let this = self.clone(); + rt.block_on(async move { this.fetch_events(filter, timeout).await }) + } +} diff --git a/net-core/src/nostr_client/manager.rs b/net-core/src/nostr_client/manager.rs @@ -43,7 +43,7 @@ impl NostrClientManager { async move { use futures::StreamExt; - let mut since = since_unix.unwrap_or_else(|| RadrootsNostrTimestamp::now().as_u64()); + let mut since = since_unix.unwrap_or_else(|| RadrootsNostrTimestamp::now().as_u64()); loop { let filter = radroots_nostr_post_events_filter(None, Some(since)); @@ -59,11 +59,7 @@ impl NostrClientManager { } }; - while let Some((_, event)) = stream.next().await { - let event = match event { - Ok(ev) => ev, - Err(_) => continue, - }; + while let Some(event) = stream.next().await { let meta = radroots_nostr::event_adapters::to_post_event_metadata(&event); let ts = event.created_at.as_u64(); since = ts.saturating_add(1); diff --git a/net-core/src/nostr_client/mod.rs b/net-core/src/nostr_client/mod.rs @@ -2,6 +2,7 @@ mod connection; mod inner; mod manager; mod events { + mod custom; mod post; mod profile; } diff --git a/tangle-schema/bindings/ts/src/types.ts b/tangle-schema/bindings/ts/src/types.ts @@ -4,6 +4,8 @@ import type { IResult, IResultList, IResultPass } from "@radroots/types-bindings export type Farm = { id: string, created_at: string, updated_at: string, name: string, area: string | null, area_unit: string | null, title: string | null, description: string | null, }; +export type FarmFindManyRel = never; + export type FarmQueryBindValues = { id: string, }; export type IFarmCreate = IFarmFields; @@ -24,7 +26,11 @@ export type IFarmFindMany = { filter: IFarmFieldsFilter | null, }; export type IFarmFindManyResolve = IResultList<Farm>; -export type IFarmFindOne = { on: FarmQueryBindValues, }; +export type IFarmFindOne = IFarmFindOneArgs | IFarmFindOneRelArgs; + +export type IFarmFindOneArgs = { on: FarmQueryBindValues, }; + +export type IFarmFindOneRelArgs = { rel: FarmFindManyRel, }; export type IFarmFindOneResolve = IResult<Farm>; @@ -54,7 +60,11 @@ export type ILocationGcsFindMany = { filter: ILocationGcsFieldsFilter | null, } export type ILocationGcsFindManyResolve = IResultList<LocationGcs>; -export type ILocationGcsFindOne = { on: LocationGcsQueryBindValues, } | { rel: LocationGcsFindManyRel, }; +export type ILocationGcsFindOne = ILocationGcsFindOneArgs | ILocationGcsFindOneRelArgs; + +export type ILocationGcsFindOneArgs = { on: LocationGcsQueryBindValues, }; + +export type ILocationGcsFindOneRelArgs = { rel: LocationGcsFindManyRel, }; export type ILocationGcsFindOneResolve = IResult<LocationGcs>; @@ -80,7 +90,11 @@ export type ILogErrorFindMany = { filter: ILogErrorFieldsFilter | null, }; export type ILogErrorFindManyResolve = IResultList<LogError>; -export type ILogErrorFindOne = { on: LogErrorQueryBindValues, }; +export type ILogErrorFindOne = ILogErrorFindOneArgs | ILogErrorFindOneRelArgs; + +export type ILogErrorFindOneArgs = { on: LogErrorQueryBindValues, }; + +export type ILogErrorFindOneRelArgs = { rel: LogErrorFindManyRel, }; export type ILogErrorFindOneResolve = IResult<LogError>; @@ -106,7 +120,11 @@ export type IMediaImageFindMany = { filter: IMediaImageFieldsFilter | null, } | export type IMediaImageFindManyResolve = IResultList<MediaImage>; -export type IMediaImageFindOne = { on: MediaImageQueryBindValues, }; +export type IMediaImageFindOne = IMediaImageFindOneArgs | IMediaImageFindOneRelArgs; + +export type IMediaImageFindOneArgs = { on: MediaImageQueryBindValues, }; + +export type IMediaImageFindOneRelArgs = { rel: MediaImageFindManyRel, }; export type IMediaImageFindOneResolve = IResult<MediaImage>; @@ -132,7 +150,11 @@ export type INostrProfileFindMany = { filter: INostrProfileFieldsFilter | null, export type INostrProfileFindManyResolve = IResultList<NostrProfile>; -export type INostrProfileFindOne = { on: NostrProfileQueryBindValues, }; +export type INostrProfileFindOne = INostrProfileFindOneArgs | INostrProfileFindOneRelArgs; + +export type INostrProfileFindOneArgs = { on: NostrProfileQueryBindValues, }; + +export type INostrProfileFindOneRelArgs = { rel: NostrProfileFindManyRel, }; export type INostrProfileFindOneResolve = IResult<NostrProfile>; @@ -162,7 +184,11 @@ export type INostrRelayFindMany = { filter: INostrRelayFieldsFilter | null, } | export type INostrRelayFindManyResolve = IResultList<NostrRelay>; -export type INostrRelayFindOne = { on: NostrRelayQueryBindValues, }; +export type INostrRelayFindOne = INostrRelayFindOneArgs | INostrRelayFindOneRelArgs; + +export type INostrRelayFindOneArgs = { on: NostrRelayQueryBindValues, }; + +export type INostrRelayFindOneRelArgs = { rel: NostrRelayFindManyRel, }; export type INostrRelayFindOneResolve = IResult<NostrRelay>; @@ -188,7 +214,11 @@ export type ITradeProductFindMany = { filter: ITradeProductFieldsFilter | null, export type ITradeProductFindManyResolve = IResultList<TradeProduct>; -export type ITradeProductFindOne = { on: TradeProductQueryBindValues, }; +export type ITradeProductFindOne = ITradeProductFindOneArgs | ITradeProductFindOneRelArgs; + +export type ITradeProductFindOneArgs = { on: TradeProductQueryBindValues, }; + +export type ITradeProductFindOneRelArgs = { rel: TradeProductFindManyRel, }; export type ITradeProductFindOneResolve = IResult<TradeProduct>; @@ -214,6 +244,8 @@ export type LocationGcsTradeProductArgs = { id: string, }; export type LogError = { id: string, created_at: string, updated_at: string, error: string, message: string, stack_trace: string | null, cause: string | null, app_system: string, app_version: string, nostr_pubkey: string, data: string | null, }; +export type LogErrorFindManyRel = never; + export type LogErrorQueryBindValues = { id: string, } | { nostr_pubkey: string, }; export type MediaImage = { id: string, created_at: string, updated_at: string, file_path: string, mime_type: string, res_base: string, res_path: string, label: string | null, description: string | null, }; @@ -242,4 +274,6 @@ export type NostrRelayQueryBindValues = { id: string, } | { url: string, }; export type TradeProduct = { id: string, created_at: string, updated_at: string, key: string, category: string, title: string, summary: string, process: string, lot: string, profile: string, year: bigint, qty_amt: bigint, qty_unit: string, qty_label: string | null, qty_avail: bigint | null, price_amt: number, price_currency: string, price_qty_amt: number, price_qty_unit: string, notes: string | null, }; +export type TradeProductFindManyRel = never; + export type TradeProductQueryBindValues = { id: string, }; diff --git a/tangle-schema/src/models/farm.rs b/tangle-schema/src/models/farm.rs @@ -93,6 +93,13 @@ impl FarmQueryBindValues { } } #[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub enum FarmFindManyRel { + +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", ts( @@ -117,15 +124,31 @@ pub type IFarmCreate = IFarmFields; pub struct IFarmCreateResolveTs; pub type IFarmCreateResolve = IResult<Farm>; #[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IFarmFindOneArgs { + pub on: FarmQueryBindValues, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IFarmFindOneRelArgs { + pub rel: FarmFindManyRel, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", ts(export, export_to = "types.ts", rename = "IFarmFindOne") )] #[derive(Deserialize, Serialize)] -pub struct IFarmFindOneArgs { - pub on: FarmQueryBindValues, +#[serde(untagged)] +pub enum IFarmFindOne { + On(IFarmFindOneArgs), + Rel(IFarmFindOneRelArgs), } -pub type IFarmFindOne = IFarmFindOneArgs; + #[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", @@ -171,7 +194,7 @@ pub type IFarmFindManyResolve = IResultList<Farm>; ) )] pub struct IFarmDeleteTs; -pub type IFarmDelete = IFarmFindOneArgs; +pub type IFarmDelete = IFarmFindOne; #[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", diff --git a/tangle-schema/src/models/location_gcs.rs b/tangle-schema/src/models/location_gcs.rs @@ -209,10 +209,20 @@ pub type ILocationGcsCreate = ILocationGcsFields; )] pub struct ILocationGcsCreateResolveTs; pub type ILocationGcsCreateResolve = IResult<LocationGcs>; +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] #[derive(Deserialize, Serialize)] pub struct ILocationGcsFindOneArgs { pub on: LocationGcsQueryBindValues, } + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct ILocationGcsFindOneRelArgs { + pub rel: LocationGcsFindManyRel, +} + #[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", @@ -221,13 +231,10 @@ pub struct ILocationGcsFindOneArgs { #[derive(Deserialize, Serialize)] #[serde(untagged)] pub enum ILocationGcsFindOne { - On { - on: LocationGcsQueryBindValues, - }, - Rel { - rel: LocationGcsFindManyRel, - }, + On(ILocationGcsFindOneArgs), + Rel(ILocationGcsFindOneRelArgs), } + #[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", @@ -313,4 +320,4 @@ pub type ILocationGcsUpdate = ILocationGcsUpdateArgs; ) )] pub struct ILocationGcsUpdateResolveTs; -pub type ILocationGcsUpdateResolve = IResult<LocationGcs>; +pub type ILocationGcsUpdateResolve = IResult<LocationGcs>; +\ No newline at end of file diff --git a/tangle-schema/src/models/log_error.rs b/tangle-schema/src/models/log_error.rs @@ -114,6 +114,13 @@ impl LogErrorQueryBindValues { } } #[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub enum LogErrorFindManyRel { + +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", ts( @@ -138,15 +145,31 @@ pub type ILogErrorCreate = ILogErrorFields; pub struct ILogErrorCreateResolveTs; pub type ILogErrorCreateResolve = IResult<LogError>; #[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct ILogErrorFindOneArgs { + pub on: LogErrorQueryBindValues, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct ILogErrorFindOneRelArgs { + pub rel: LogErrorFindManyRel, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", ts(export, export_to = "types.ts", rename = "ILogErrorFindOne") )] #[derive(Deserialize, Serialize)] -pub struct ILogErrorFindOneArgs { - pub on: LogErrorQueryBindValues, +#[serde(untagged)] +pub enum ILogErrorFindOne { + On(ILogErrorFindOneArgs), + Rel(ILogErrorFindOneRelArgs), } -pub type ILogErrorFindOne = ILogErrorFindOneArgs; + #[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", @@ -192,7 +215,7 @@ pub type ILogErrorFindManyResolve = IResultList<LogError>; ) )] pub struct ILogErrorDeleteTs; -pub type ILogErrorDelete = ILogErrorFindOneArgs; +pub type ILogErrorDelete = ILogErrorFindOne; #[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", diff --git a/tangle-schema/src/models/media_image.rs b/tangle-schema/src/models/media_image.rs @@ -142,15 +142,31 @@ pub type IMediaImageCreate = IMediaImageFields; pub struct IMediaImageCreateResolveTs; pub type IMediaImageCreateResolve = IResult<MediaImage>; #[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IMediaImageFindOneArgs { + pub on: MediaImageQueryBindValues, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct IMediaImageFindOneRelArgs { + pub rel: MediaImageFindManyRel, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", ts(export, export_to = "types.ts", rename = "IMediaImageFindOne") )] #[derive(Deserialize, Serialize)] -pub struct IMediaImageFindOneArgs { - pub on: MediaImageQueryBindValues, +#[serde(untagged)] +pub enum IMediaImageFindOne { + On(IMediaImageFindOneArgs), + Rel(IMediaImageFindOneRelArgs), } -pub type IMediaImageFindOne = IMediaImageFindOneArgs; + #[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", @@ -201,7 +217,7 @@ pub type IMediaImageFindManyResolve = IResultList<MediaImage>; ) )] pub struct IMediaImageDeleteTs; -pub type IMediaImageDelete = IMediaImageFindOneArgs; +pub type IMediaImageDelete = IMediaImageFindOne; #[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", diff --git a/tangle-schema/src/models/nostr_profile.rs b/tangle-schema/src/models/nostr_profile.rs @@ -172,15 +172,31 @@ pub type INostrProfileCreate = INostrProfileFields; pub struct INostrProfileCreateResolveTs; pub type INostrProfileCreateResolve = IResult<NostrProfile>; #[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct INostrProfileFindOneArgs { + pub on: NostrProfileQueryBindValues, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct INostrProfileFindOneRelArgs { + pub rel: NostrProfileFindManyRel, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", ts(export, export_to = "types.ts", rename = "INostrProfileFindOne") )] #[derive(Deserialize, Serialize)] -pub struct INostrProfileFindOneArgs { - pub on: NostrProfileQueryBindValues, +#[serde(untagged)] +pub enum INostrProfileFindOne { + On(INostrProfileFindOneArgs), + Rel(INostrProfileFindOneRelArgs), } -pub type INostrProfileFindOne = INostrProfileFindOneArgs; + #[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", @@ -231,7 +247,7 @@ pub type INostrProfileFindManyResolve = IResultList<NostrProfile>; ) )] pub struct INostrProfileDeleteTs; -pub type INostrProfileDelete = INostrProfileFindOneArgs; +pub type INostrProfileDelete = INostrProfileFindOne; #[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", diff --git a/tangle-schema/src/models/nostr_relay.rs b/tangle-schema/src/models/nostr_relay.rs @@ -173,15 +173,31 @@ pub type INostrRelayCreate = INostrRelayFields; pub struct INostrRelayCreateResolveTs; pub type INostrRelayCreateResolve = IResult<NostrRelay>; #[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct INostrRelayFindOneArgs { + pub on: NostrRelayQueryBindValues, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct INostrRelayFindOneRelArgs { + pub rel: NostrRelayFindManyRel, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", ts(export, export_to = "types.ts", rename = "INostrRelayFindOne") )] #[derive(Deserialize, Serialize)] -pub struct INostrRelayFindOneArgs { - pub on: NostrRelayQueryBindValues, +#[serde(untagged)] +pub enum INostrRelayFindOne { + On(INostrRelayFindOneArgs), + Rel(INostrRelayFindOneRelArgs), } -pub type INostrRelayFindOne = INostrRelayFindOneArgs; + #[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", @@ -232,7 +248,7 @@ pub type INostrRelayFindManyResolve = IResultList<NostrRelay>; ) )] pub struct INostrRelayDeleteTs; -pub type INostrRelayDelete = INostrRelayFindOneArgs; +pub type INostrRelayDelete = INostrRelayFindOne; #[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", diff --git a/tangle-schema/src/models/trade_product.rs b/tangle-schema/src/models/trade_product.rs @@ -164,6 +164,13 @@ impl TradeProductQueryBindValues { } } #[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Clone, Deserialize, Serialize)] +pub enum TradeProductFindManyRel { + +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", ts( @@ -188,15 +195,31 @@ pub type ITradeProductCreate = ITradeProductFields; pub struct ITradeProductCreateResolveTs; pub type ITradeProductCreateResolve = IResult<TradeProduct>; #[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct ITradeProductFindOneArgs { + pub on: TradeProductQueryBindValues, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] +#[cfg_attr(feature = "ts-rs", ts(export, export_to = "types.ts"))] +#[derive(Deserialize, Serialize)] +pub struct ITradeProductFindOneRelArgs { + pub rel: TradeProductFindManyRel, +} + +#[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", ts(export, export_to = "types.ts", rename = "ITradeProductFindOne") )] #[derive(Deserialize, Serialize)] -pub struct ITradeProductFindOneArgs { - pub on: TradeProductQueryBindValues, +#[serde(untagged)] +pub enum ITradeProductFindOne { + On(ITradeProductFindOneArgs), + Rel(ITradeProductFindOneRelArgs), } -pub type ITradeProductFindOne = ITradeProductFindOneArgs; + #[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", @@ -242,7 +265,7 @@ pub type ITradeProductFindManyResolve = IResultList<TradeProduct>; ) )] pub struct ITradeProductDeleteTs; -pub type ITradeProductDelete = ITradeProductFindOneArgs; +pub type ITradeProductDelete = ITradeProductFindOne; #[cfg_attr(feature = "ts-rs", derive(TS))] #[cfg_attr( feature = "ts-rs", diff --git a/tangle-sql/migrations/0000_init.up.sql b/tangle-sql/migrations/0000_init.up.sql @@ -2,4 +2,4 @@ CREATE TABLE IF NOT EXISTS __migrations ( id INTEGER PRIMARY KEY, name TEXT NOT NULL UNIQUE, applied_at TEXT NOT NULL DEFAULT (datetime('now')) -); +); +\ No newline at end of file diff --git a/tangle-sql/src/models/farm.rs b/tangle-sql/src/models/farm.rs @@ -13,6 +13,7 @@ use radroots_tangle_schema::farm::{ IFarmUpdate, IFarmUpdateResolve, Farm, + FarmFindManyRel, FarmQueryBindValues, }; use radroots_types::types::{IError, IResult, IResultList}; @@ -35,12 +36,8 @@ pub fn create<E: SqlExecutor>( let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); let params_json = utils::to_params_json(bind_values)?; let _ = exec.exec(&sql, &params_json)?; - let args = IFarmFindOne { - on: FarmQueryBindValues::Id { id: id.clone() }, - }; - let found = find_one(exec, &args)?; - let result = found - .result + let on = FarmQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } @@ -49,12 +46,10 @@ pub fn find_one<E: SqlExecutor>( exec: &E, opts: &IFarmFindOne, ) -> Result<IFarmFindOneResolve, IError<SqlError>> { - let (column, value) = opts.on.to_filter_param(); - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; - let json = exec.query_raw(&sql, &params_json)?; - let mut rows: Vec<Farm> = utils::parse_json(&json)?; - let result = rows.pop(); + let result = match opts { + IFarmFindOne::On(args) => find_one_by_on(exec, &args.on)?, + IFarmFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; Ok(IResult { result }) } @@ -77,6 +72,34 @@ fn find_many_filter<E: SqlExecutor>( Ok(rows) } +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &FarmQueryBindValues, +) -> Result<Option<Farm>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, &params_json)?; + let mut rows: Vec<Farm> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &FarmFindManyRel) -> (&'static str, Vec<Value>) { + match *rel {} +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &FarmFindManyRel, +) -> Result<Option<Farm>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, &params_json)?; + let mut rows: Vec<Farm> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<Farm, IError<SqlError>> { let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); @@ -107,11 +130,8 @@ pub fn update<E: SqlExecutor>( let id_for_lookup = match opts.on.primary_key() { Some(id) => id, None => { - let find_opts = IFarmFindOne { - on: opts.on.clone(), - }; - let found = find_one(exec, &find_opts)?; - let model = found.result.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; model.id } }; @@ -130,14 +150,18 @@ pub fn delete<E: SqlExecutor>( exec: &E, opts: &IFarmDelete, ) -> Result<IFarmDeleteResolve, IError<SqlError>> { - let id_for_lookup = match opts.on.primary_key() { - Some(id) => id, - None => { - let find_opts = IFarmFindOne { - on: opts.on.clone(), - }; - let found = find_one(exec, &find_opts)?; - let model = found.result.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + let id_for_lookup = match opts { + IFarmDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + IFarmDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; model.id } }; @@ -149,3 +173,7 @@ pub fn delete<E: SqlExecutor>( } Ok(IResult { result: id_for_lookup }) } + +fn rel_lookup_key(rel: &FarmFindManyRel) -> String { + match *rel {} +} diff --git a/tangle-sql/src/models/farm_location.rs b/tangle-sql/src/models/farm_location.rs @@ -13,7 +13,7 @@ pub fn set<E: SqlExecutor>( exec: &E, opts: &IFarmLocationRelation, ) -> Result<IFarmLocationResolve, IError<SqlError>> { - let mut query_vals: Vec<Value> = Vec::new(); + let mut query_vals: Vec<Value> = Vec::with_capacity(2); let (farm_column, farm_value) = opts.farm.to_filter_param(); query_vals.push(farm_value); let (location_gcs_column, location_gcs_value) = opts.location_gcs.to_filter_param(); @@ -28,7 +28,7 @@ pub fn unset<E: SqlExecutor>( exec: &E, opts: &IFarmLocationRelation, ) -> Result<IFarmLocationResolve, IError<SqlError>> { - let mut query_vals: Vec<Value> = Vec::new(); + let mut query_vals: Vec<Value> = Vec::with_capacity(2); let (farm_column, farm_value) = opts.farm.to_filter_param(); query_vals.push(farm_value); let (location_gcs_column, location_gcs_value) = opts.location_gcs.to_filter_param(); diff --git a/tangle-sql/src/models/location_gcs.rs b/tangle-sql/src/models/location_gcs.rs @@ -36,12 +36,8 @@ pub fn create<E: SqlExecutor>( let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); let params_json = utils::to_params_json(bind_values)?; let _ = exec.exec(&sql, &params_json)?; - let args = ILocationGcsFindOne::On { - on: LocationGcsQueryBindValues::Id { id: id.clone() }, - }; - let found = find_one(exec, &args)?; - let result = found - .result + let on = LocationGcsQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } @@ -51,15 +47,8 @@ pub fn find_one<E: SqlExecutor>( opts: &ILocationGcsFindOne, ) -> Result<ILocationGcsFindOneResolve, IError<SqlError>> { let result = match opts { - ILocationGcsFindOne::On { on } => { - let (column, value) = on.to_filter_param(); - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; - let json = exec.query_raw(&sql, &params_json)?; - let mut rows: Vec<LocationGcs> = utils::parse_json(&json)?; - rows.pop() - } - ILocationGcsFindOne::Rel { rel } => find_one_by_rel(exec, rel)?, + ILocationGcsFindOne::On(args) => find_one_by_on(exec, &args.on)?, + ILocationGcsFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, }; Ok(IResult { result }) } @@ -86,6 +75,18 @@ fn find_many_filter<E: SqlExecutor>( Ok(rows) } +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &LocationGcsQueryBindValues, +) -> Result<Option<LocationGcs>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, &params_json)?; + let mut rows: Vec<LocationGcs> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + fn rel_query(rel: &LocationGcsFindManyRel) -> (&'static str, Vec<Value>) { match rel { LocationGcsFindManyRel::OnTradeProduct(args) => ( @@ -161,11 +162,8 @@ pub fn update<E: SqlExecutor>( let id_for_lookup = match opts.on.primary_key() { Some(id) => id, None => { - let find_opts = ILocationGcsFindOne::On { - on: opts.on.clone(), - }; - let found = find_one(exec, &find_opts)?; - let model = found.result.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; model.id } }; @@ -185,20 +183,17 @@ pub fn delete<E: SqlExecutor>( opts: &ILocationGcsDelete, ) -> Result<ILocationGcsDeleteResolve, IError<SqlError>> { let id_for_lookup = match opts { - ILocationGcsDelete::On { on } => match on.primary_key() { + ILocationGcsDelete::On(args) => match args.on.primary_key() { Some(id) => id, None => { - let find_opts = ILocationGcsFindOne::On { on: on.clone() }; - let found = find_one(exec, &find_opts)?; - let model = found - .result - .ok_or_else(|| IError::from(SqlError::NotFound(on.lookup_key())))?; + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; model.id } }, - ILocationGcsDelete::Rel { rel } => { - let found = find_one_by_rel(exec, rel)?; - let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(rel))))?; + ILocationGcsDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; model.id } }; diff --git a/tangle-sql/src/models/log_error.rs b/tangle-sql/src/models/log_error.rs @@ -13,6 +13,7 @@ use radroots_tangle_schema::log_error::{ ILogErrorUpdate, ILogErrorUpdateResolve, LogError, + LogErrorFindManyRel, LogErrorQueryBindValues, }; use radroots_types::types::{IError, IResult, IResultList}; @@ -35,12 +36,8 @@ pub fn create<E: SqlExecutor>( let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); let params_json = utils::to_params_json(bind_values)?; let _ = exec.exec(&sql, &params_json)?; - let args = ILogErrorFindOne { - on: LogErrorQueryBindValues::Id { id: id.clone() }, - }; - let found = find_one(exec, &args)?; - let result = found - .result + let on = LogErrorQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } @@ -49,12 +46,10 @@ pub fn find_one<E: SqlExecutor>( exec: &E, opts: &ILogErrorFindOne, ) -> Result<ILogErrorFindOneResolve, IError<SqlError>> { - let (column, value) = opts.on.to_filter_param(); - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; - let json = exec.query_raw(&sql, &params_json)?; - let mut rows: Vec<LogError> = utils::parse_json(&json)?; - let result = rows.pop(); + let result = match opts { + ILogErrorFindOne::On(args) => find_one_by_on(exec, &args.on)?, + ILogErrorFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; Ok(IResult { result }) } @@ -77,6 +72,34 @@ fn find_many_filter<E: SqlExecutor>( Ok(rows) } +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &LogErrorQueryBindValues, +) -> Result<Option<LogError>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, &params_json)?; + let mut rows: Vec<LogError> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &LogErrorFindManyRel) -> (&'static str, Vec<Value>) { + match *rel {} +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &LogErrorFindManyRel, +) -> Result<Option<LogError>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, &params_json)?; + let mut rows: Vec<LogError> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<LogError, IError<SqlError>> { let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); @@ -107,11 +130,8 @@ pub fn update<E: SqlExecutor>( let id_for_lookup = match opts.on.primary_key() { Some(id) => id, None => { - let find_opts = ILogErrorFindOne { - on: opts.on.clone(), - }; - let found = find_one(exec, &find_opts)?; - let model = found.result.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; model.id } }; @@ -130,14 +150,18 @@ pub fn delete<E: SqlExecutor>( exec: &E, opts: &ILogErrorDelete, ) -> Result<ILogErrorDeleteResolve, IError<SqlError>> { - let id_for_lookup = match opts.on.primary_key() { - Some(id) => id, - None => { - let find_opts = ILogErrorFindOne { - on: opts.on.clone(), - }; - let found = find_one(exec, &find_opts)?; - let model = found.result.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + let id_for_lookup = match opts { + ILogErrorDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + ILogErrorDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; model.id } }; @@ -149,3 +173,7 @@ pub fn delete<E: SqlExecutor>( } Ok(IResult { result: id_for_lookup }) } + +fn rel_lookup_key(rel: &LogErrorFindManyRel) -> String { + match *rel {} +} diff --git a/tangle-sql/src/models/media_image.rs b/tangle-sql/src/models/media_image.rs @@ -36,12 +36,8 @@ pub fn create<E: SqlExecutor>( let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); let params_json = utils::to_params_json(bind_values)?; let _ = exec.exec(&sql, &params_json)?; - let args = IMediaImageFindOne { - on: MediaImageQueryBindValues::Id { id: id.clone() }, - }; - let found = find_one(exec, &args)?; - let result = found - .result + let on = MediaImageQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } @@ -50,12 +46,10 @@ pub fn find_one<E: SqlExecutor>( exec: &E, opts: &IMediaImageFindOne, ) -> Result<IMediaImageFindOneResolve, IError<SqlError>> { - let (column, value) = opts.on.to_filter_param(); - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; - let json = exec.query_raw(&sql, &params_json)?; - let mut rows: Vec<MediaImage> = utils::parse_json(&json)?; - let result = rows.pop(); + let result = match opts { + IMediaImageFindOne::On(args) => find_one_by_on(exec, &args.on)?, + IMediaImageFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; Ok(IResult { result }) } @@ -81,23 +75,50 @@ fn find_many_filter<E: SqlExecutor>( Ok(rows) } +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &MediaImageQueryBindValues, +) -> Result<Option<MediaImage>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, &params_json)?; + let mut rows: Vec<MediaImage> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &MediaImageFindManyRel) -> (&'static str, Vec<Value>) { + match rel { + MediaImageFindManyRel::OnTradeProduct(args) => ( + "SELECT mu.* FROM media_image mu JOIN trade_product_media tp_lg ON mu.id = tp_lg.tb_mu WHERE tp_lg.tb_tp = ?", + vec![Value::from(args.id.clone())], + ), + MediaImageFindManyRel::OffTradeProduct(args) => ( + "SELECT mu.* FROM media_image mu WHERE NOT EXISTS (SELECT 1 FROM trade_product_media tp_lg WHERE tp_lg.tb_mu = mu.id AND tp_lg.tb_tp = ?)", + vec![Value::from(args.id.clone())], + ), + } +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &MediaImageFindManyRel, +) -> Result<Option<MediaImage>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, &params_json)?; + let mut rows: Vec<MediaImage> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + fn find_many_by_rel<E: SqlExecutor>( exec: &E, rel: &MediaImageFindManyRel, ) -> Result<Vec<MediaImage>, IError<SqlError>> { - let (sql, bind_values): (String, Vec<Value>) = match rel { - MediaImageFindManyRel::OnTradeProduct(args) => { - let sql = String::from("SELECT mu.* FROM media_image mu JOIN trade_product_media tp_lg ON mu.id = tp_lg.tb_mu WHERE tp_lg.tb_tp = ?;"); - let binds = vec![Value::from(args.id.clone())]; - (sql, binds) - } - MediaImageFindManyRel::OffTradeProduct(args) => { - let sql = String::from("SELECT mu.* FROM media_image mu WHERE NOT EXISTS (SELECT 1 FROM trade_product_media tp_lg WHERE tp_lg.tb_mu = mu.id AND tp_lg.tb_tp = ?);"); - let binds = vec![Value::from(args.id.clone())]; - (sql, binds) - } - }; + let (sql, bind_values) = rel_query(rel); let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql};"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<MediaImage> = utils::parse_json(&json)?; Ok(rows) @@ -133,11 +154,8 @@ pub fn update<E: SqlExecutor>( let id_for_lookup = match opts.on.primary_key() { Some(id) => id, None => { - let find_opts = IMediaImageFindOne { - on: opts.on.clone(), - }; - let found = find_one(exec, &find_opts)?; - let model = found.result.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; model.id } }; @@ -156,14 +174,18 @@ pub fn delete<E: SqlExecutor>( exec: &E, opts: &IMediaImageDelete, ) -> Result<IMediaImageDeleteResolve, IError<SqlError>> { - let id_for_lookup = match opts.on.primary_key() { - Some(id) => id, - None => { - let find_opts = IMediaImageFindOne { - on: opts.on.clone(), - }; - let found = find_one(exec, &find_opts)?; - let model = found.result.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + let id_for_lookup = match opts { + IMediaImageDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + IMediaImageDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; model.id } }; @@ -175,3 +197,10 @@ pub fn delete<E: SqlExecutor>( } Ok(IResult { result: id_for_lookup }) } + +fn rel_lookup_key(rel: &MediaImageFindManyRel) -> String { + match rel { + MediaImageFindManyRel::OnTradeProduct(args) => format!("on_trade_product:{}", args.id.as_str()), + MediaImageFindManyRel::OffTradeProduct(args) => format!("off_trade_product:{}", args.id.as_str()), + } +} diff --git a/tangle-sql/src/models/nostr_profile.rs b/tangle-sql/src/models/nostr_profile.rs @@ -36,12 +36,8 @@ pub fn create<E: SqlExecutor>( let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); let params_json = utils::to_params_json(bind_values)?; let _ = exec.exec(&sql, &params_json)?; - let args = INostrProfileFindOne { - on: NostrProfileQueryBindValues::Id { id: id.clone() }, - }; - let found = find_one(exec, &args)?; - let result = found - .result + let on = NostrProfileQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } @@ -50,12 +46,10 @@ pub fn find_one<E: SqlExecutor>( exec: &E, opts: &INostrProfileFindOne, ) -> Result<INostrProfileFindOneResolve, IError<SqlError>> { - let (column, value) = opts.on.to_filter_param(); - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; - let json = exec.query_raw(&sql, &params_json)?; - let mut rows: Vec<NostrProfile> = utils::parse_json(&json)?; - let result = rows.pop(); + let result = match opts { + INostrProfileFindOne::On(args) => find_one_by_on(exec, &args.on)?, + INostrProfileFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; Ok(IResult { result }) } @@ -81,23 +75,50 @@ fn find_many_filter<E: SqlExecutor>( Ok(rows) } +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &NostrProfileQueryBindValues, +) -> Result<Option<NostrProfile>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, &params_json)?; + let mut rows: Vec<NostrProfile> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &NostrProfileFindManyRel) -> (&'static str, Vec<Value>) { + match rel { + NostrProfileFindManyRel::OnRelay(args) => ( + "SELECT pr.* FROM nostr_profile pr JOIN nostr_profile_relay pr_rl ON pr.id = pr_rl.tb_pr WHERE pr_rl.tb_rl = ?", + vec![Value::from(args.id.clone())], + ), + NostrProfileFindManyRel::OffRelay(args) => ( + "SELECT pr.* FROM nostr_profile pr WHERE NOT EXISTS (SELECT 1 FROM nostr_profile_relay pr_rl WHERE pr_rl.tb_pr = pr.id AND pr_rl.tb_rl = ?)", + vec![Value::from(args.id.clone())], + ), + } +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &NostrProfileFindManyRel, +) -> Result<Option<NostrProfile>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, &params_json)?; + let mut rows: Vec<NostrProfile> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + fn find_many_by_rel<E: SqlExecutor>( exec: &E, rel: &NostrProfileFindManyRel, ) -> Result<Vec<NostrProfile>, IError<SqlError>> { - let (sql, bind_values): (String, Vec<Value>) = match rel { - NostrProfileFindManyRel::OnRelay(args) => { - let sql = String::from("SELECT pr.* FROM nostr_profile pr JOIN nostr_profile_relay pr_rl ON pr.id = pr_rl.tb_pr WHERE pr_rl.tb_rl = ?;"); - let binds = vec![Value::from(args.id.clone())]; - (sql, binds) - } - NostrProfileFindManyRel::OffRelay(args) => { - let sql = String::from("SELECT pr.* FROM nostr_profile pr WHERE NOT EXISTS (SELECT 1 FROM nostr_profile_relay pr_rl WHERE pr_rl.tb_pr = pr.id AND pr_rl.tb_rl = ?);"); - let binds = vec![Value::from(args.id.clone())]; - (sql, binds) - } - }; + let (sql, bind_values) = rel_query(rel); let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql};"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<NostrProfile> = utils::parse_json(&json)?; Ok(rows) @@ -133,11 +154,8 @@ pub fn update<E: SqlExecutor>( let id_for_lookup = match opts.on.primary_key() { Some(id) => id, None => { - let find_opts = INostrProfileFindOne { - on: opts.on.clone(), - }; - let found = find_one(exec, &find_opts)?; - let model = found.result.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; model.id } }; @@ -156,14 +174,18 @@ pub fn delete<E: SqlExecutor>( exec: &E, opts: &INostrProfileDelete, ) -> Result<INostrProfileDeleteResolve, IError<SqlError>> { - let id_for_lookup = match opts.on.primary_key() { - Some(id) => id, - None => { - let find_opts = INostrProfileFindOne { - on: opts.on.clone(), - }; - let found = find_one(exec, &find_opts)?; - let model = found.result.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + let id_for_lookup = match opts { + INostrProfileDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + INostrProfileDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; model.id } }; @@ -175,3 +197,10 @@ pub fn delete<E: SqlExecutor>( } Ok(IResult { result: id_for_lookup }) } + +fn rel_lookup_key(rel: &NostrProfileFindManyRel) -> String { + match rel { + NostrProfileFindManyRel::OnRelay(args) => format!("on_relay:{}", args.id.as_str()), + NostrProfileFindManyRel::OffRelay(args) => format!("off_relay:{}", args.id.as_str()), + } +} diff --git a/tangle-sql/src/models/nostr_profile_relay.rs b/tangle-sql/src/models/nostr_profile_relay.rs @@ -13,7 +13,7 @@ pub fn set<E: SqlExecutor>( exec: &E, opts: &INostrProfileRelayRelation, ) -> Result<INostrProfileRelayResolve, IError<SqlError>> { - let mut query_vals: Vec<Value> = Vec::new(); + let mut query_vals: Vec<Value> = Vec::with_capacity(2); let (nostr_profile_column, nostr_profile_value) = opts.nostr_profile.to_filter_param(); query_vals.push(nostr_profile_value); let (nostr_relay_column, nostr_relay_value) = opts.nostr_relay.to_filter_param(); @@ -28,7 +28,7 @@ pub fn unset<E: SqlExecutor>( exec: &E, opts: &INostrProfileRelayRelation, ) -> Result<INostrProfileRelayResolve, IError<SqlError>> { - let mut query_vals: Vec<Value> = Vec::new(); + let mut query_vals: Vec<Value> = Vec::with_capacity(2); let (nostr_profile_column, nostr_profile_value) = opts.nostr_profile.to_filter_param(); query_vals.push(nostr_profile_value); let (nostr_relay_column, nostr_relay_value) = opts.nostr_relay.to_filter_param(); diff --git a/tangle-sql/src/models/nostr_relay.rs b/tangle-sql/src/models/nostr_relay.rs @@ -36,12 +36,8 @@ pub fn create<E: SqlExecutor>( let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); let params_json = utils::to_params_json(bind_values)?; let _ = exec.exec(&sql, &params_json)?; - let args = INostrRelayFindOne { - on: NostrRelayQueryBindValues::Id { id: id.clone() }, - }; - let found = find_one(exec, &args)?; - let result = found - .result + let on = NostrRelayQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } @@ -50,12 +46,10 @@ pub fn find_one<E: SqlExecutor>( exec: &E, opts: &INostrRelayFindOne, ) -> Result<INostrRelayFindOneResolve, IError<SqlError>> { - let (column, value) = opts.on.to_filter_param(); - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; - let json = exec.query_raw(&sql, &params_json)?; - let mut rows: Vec<NostrRelay> = utils::parse_json(&json)?; - let result = rows.pop(); + let result = match opts { + INostrRelayFindOne::On(args) => find_one_by_on(exec, &args.on)?, + INostrRelayFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; Ok(IResult { result }) } @@ -81,23 +75,50 @@ fn find_many_filter<E: SqlExecutor>( Ok(rows) } +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &NostrRelayQueryBindValues, +) -> Result<Option<NostrRelay>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, &params_json)?; + let mut rows: Vec<NostrRelay> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &NostrRelayFindManyRel) -> (&'static str, Vec<Value>) { + match rel { + NostrRelayFindManyRel::OnProfile(args) => ( + "SELECT rl.* FROM nostr_relay rl JOIN nostr_profile_relay pr_rl ON rl.id = pr_rl.tb_rl JOIN nostr_profile pr ON pr.id = pr_rl.tb_pr WHERE pr.public_key = ?", + vec![Value::from(args.public_key.clone())], + ), + NostrRelayFindManyRel::OffProfile(args) => ( + "SELECT rl.* FROM nostr_relay rl LEFT JOIN nostr_profile_relay pr_rl ON rl.id = pr_rl.tb_rl LEFT JOIN nostr_profile pr ON pr.id = pr_rl.tb_pr WHERE pr.public_key <> ?", + vec![Value::from(args.public_key.clone())], + ), + } +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &NostrRelayFindManyRel, +) -> Result<Option<NostrRelay>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, &params_json)?; + let mut rows: Vec<NostrRelay> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + fn find_many_by_rel<E: SqlExecutor>( exec: &E, rel: &NostrRelayFindManyRel, ) -> Result<Vec<NostrRelay>, IError<SqlError>> { - let (sql, bind_values): (String, Vec<Value>) = match rel { - NostrRelayFindManyRel::OnProfile(args) => { - let sql = String::from("SELECT rl.* FROM nostr_relay rl JOIN nostr_profile_relay pr_rl ON rl.id = pr_rl.tb_rl JOIN nostr_profile pr ON pr.id = pr_rl.tb_pr WHERE pr.public_key = ?;"); - let binds = vec![Value::from(args.public_key.clone())]; - (sql, binds) - } - NostrRelayFindManyRel::OffProfile(args) => { - let sql = String::from("SELECT rl.* FROM nostr_relay rl LEFT JOIN nostr_profile_relay pr_rl ON rl.id = pr_rl.tb_rl LEFT JOIN nostr_profile pr ON pr.id = pr_rl.tb_pr WHERE pr.public_key <> ?;"); - let binds = vec![Value::from(args.public_key.clone())]; - (sql, binds) - } - }; + let (sql, bind_values) = rel_query(rel); let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql};"); let json = exec.query_raw(&sql, &params_json)?; let rows: Vec<NostrRelay> = utils::parse_json(&json)?; Ok(rows) @@ -133,11 +154,8 @@ pub fn update<E: SqlExecutor>( let id_for_lookup = match opts.on.primary_key() { Some(id) => id, None => { - let find_opts = INostrRelayFindOne { - on: opts.on.clone(), - }; - let found = find_one(exec, &find_opts)?; - let model = found.result.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; model.id } }; @@ -156,14 +174,18 @@ pub fn delete<E: SqlExecutor>( exec: &E, opts: &INostrRelayDelete, ) -> Result<INostrRelayDeleteResolve, IError<SqlError>> { - let id_for_lookup = match opts.on.primary_key() { - Some(id) => id, - None => { - let find_opts = INostrRelayFindOne { - on: opts.on.clone(), - }; - let found = find_one(exec, &find_opts)?; - let model = found.result.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + let id_for_lookup = match opts { + INostrRelayDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + INostrRelayDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; model.id } }; @@ -175,3 +197,10 @@ pub fn delete<E: SqlExecutor>( } Ok(IResult { result: id_for_lookup }) } + +fn rel_lookup_key(rel: &NostrRelayFindManyRel) -> String { + match rel { + NostrRelayFindManyRel::OnProfile(args) => format!("on_profile:{}", args.public_key.as_str()), + NostrRelayFindManyRel::OffProfile(args) => format!("off_profile:{}", args.public_key.as_str()), + } +} diff --git a/tangle-sql/src/models/trade_product.rs b/tangle-sql/src/models/trade_product.rs @@ -13,6 +13,7 @@ use radroots_tangle_schema::trade_product::{ ITradeProductUpdate, ITradeProductUpdateResolve, TradeProduct, + TradeProductFindManyRel, TradeProductQueryBindValues, }; use radroots_types::types::{IError, IResult, IResultList}; @@ -35,12 +36,8 @@ pub fn create<E: SqlExecutor>( let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map); let params_json = utils::to_params_json(bind_values)?; let _ = exec.exec(&sql, &params_json)?; - let args = ITradeProductFindOne { - on: TradeProductQueryBindValues::Id { id: id.clone() }, - }; - let found = find_one(exec, &args)?; - let result = found - .result + let on = TradeProductQueryBindValues::Id { id: id.clone() }; + let result = find_one_by_on(exec, &on)? .ok_or_else(|| IError::from(SqlError::NotFound(id.clone())))?; Ok(IResult { result }) } @@ -49,12 +46,10 @@ pub fn find_one<E: SqlExecutor>( exec: &E, opts: &ITradeProductFindOne, ) -> Result<ITradeProductFindOneResolve, IError<SqlError>> { - let (column, value) = opts.on.to_filter_param(); - let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); - let params_json = utils::to_params_json(vec![value])?; - let json = exec.query_raw(&sql, &params_json)?; - let mut rows: Vec<TradeProduct> = utils::parse_json(&json)?; - let result = rows.pop(); + let result = match opts { + ITradeProductFindOne::On(args) => find_one_by_on(exec, &args.on)?, + ITradeProductFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?, + }; Ok(IResult { result }) } @@ -77,6 +72,34 @@ fn find_many_filter<E: SqlExecutor>( Ok(rows) } +fn find_one_by_on<E: SqlExecutor>( + exec: &E, + on: &TradeProductQueryBindValues, +) -> Result<Option<TradeProduct>, IError<SqlError>> { + let (column, value) = on.to_filter_param(); + let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;"); + let params_json = utils::to_params_json(vec![value])?; + let json = exec.query_raw(&sql, &params_json)?; + let mut rows: Vec<TradeProduct> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + +fn rel_query(rel: &TradeProductFindManyRel) -> (&'static str, Vec<Value>) { + match *rel {} +} + +fn find_one_by_rel<E: SqlExecutor>( + exec: &E, + rel: &TradeProductFindManyRel, +) -> Result<Option<TradeProduct>, IError<SqlError>> { + let (sql, bind_values) = rel_query(rel); + let params_json = utils::to_params_json(bind_values)?; + let sql = format!("{sql} LIMIT 1;"); + let json = exec.query_raw(&sql, &params_json)?; + let mut rows: Vec<TradeProduct> = utils::parse_json(&json)?; + Ok(rows.pop()) +} + fn select_by_id<E: SqlExecutor>(exec: &E, id: &str) -> Result<TradeProduct, IError<SqlError>> { let params_json = utils::to_params_json(vec![Value::from(id.to_owned())])?; let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;"); @@ -107,11 +130,8 @@ pub fn update<E: SqlExecutor>( let id_for_lookup = match opts.on.primary_key() { Some(id) => id, None => { - let find_opts = ITradeProductFindOne { - on: opts.on.clone(), - }; - let found = find_one(exec, &find_opts)?; - let model = found.result.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + let found = find_one_by_on(exec, &opts.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; model.id } }; @@ -130,14 +150,18 @@ pub fn delete<E: SqlExecutor>( exec: &E, opts: &ITradeProductDelete, ) -> Result<ITradeProductDeleteResolve, IError<SqlError>> { - let id_for_lookup = match opts.on.primary_key() { - Some(id) => id, - None => { - let find_opts = ITradeProductFindOne { - on: opts.on.clone(), - }; - let found = find_one(exec, &find_opts)?; - let model = found.result.ok_or_else(|| IError::from(SqlError::NotFound(opts.on.lookup_key())))?; + let id_for_lookup = match opts { + ITradeProductDelete::On(args) => match args.on.primary_key() { + Some(id) => id, + None => { + let found = find_one_by_on(exec, &args.on)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(args.on.lookup_key())))?; + model.id + } + }, + ITradeProductDelete::Rel(args) => { + let found = find_one_by_rel(exec, &args.rel)?; + let model = found.ok_or_else(|| IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?; model.id } }; @@ -149,3 +173,7 @@ pub fn delete<E: SqlExecutor>( } Ok(IResult { result: id_for_lookup }) } + +fn rel_lookup_key(rel: &TradeProductFindManyRel) -> String { + match *rel {} +} diff --git a/tangle-sql/src/models/trade_product_location.rs b/tangle-sql/src/models/trade_product_location.rs @@ -13,7 +13,7 @@ pub fn set<E: SqlExecutor>( exec: &E, opts: &ITradeProductLocationRelation, ) -> Result<ITradeProductLocationResolve, IError<SqlError>> { - let mut query_vals: Vec<Value> = Vec::new(); + let mut query_vals: Vec<Value> = Vec::with_capacity(2); let (trade_product_column, trade_product_value) = opts.trade_product.to_filter_param(); query_vals.push(trade_product_value); let (location_gcs_column, location_gcs_value) = opts.location_gcs.to_filter_param(); @@ -28,7 +28,7 @@ pub fn unset<E: SqlExecutor>( exec: &E, opts: &ITradeProductLocationRelation, ) -> Result<ITradeProductLocationResolve, IError<SqlError>> { - let mut query_vals: Vec<Value> = Vec::new(); + let mut query_vals: Vec<Value> = Vec::with_capacity(2); let (trade_product_column, trade_product_value) = opts.trade_product.to_filter_param(); query_vals.push(trade_product_value); let (location_gcs_column, location_gcs_value) = opts.location_gcs.to_filter_param(); diff --git a/tangle-sql/src/models/trade_product_media.rs b/tangle-sql/src/models/trade_product_media.rs @@ -13,7 +13,7 @@ pub fn set<E: SqlExecutor>( exec: &E, opts: &ITradeProductMediaRelation, ) -> Result<ITradeProductMediaResolve, IError<SqlError>> { - let mut query_vals: Vec<Value> = Vec::new(); + let mut query_vals: Vec<Value> = Vec::with_capacity(2); let (trade_product_column, trade_product_value) = opts.trade_product.to_filter_param(); query_vals.push(trade_product_value); let (media_image_column, media_image_value) = opts.media_image.to_filter_param(); @@ -28,7 +28,7 @@ pub fn unset<E: SqlExecutor>( exec: &E, opts: &ITradeProductMediaRelation, ) -> Result<ITradeProductMediaResolve, IError<SqlError>> { - let mut query_vals: Vec<Value> = Vec::new(); + let mut query_vals: Vec<Value> = Vec::with_capacity(2); let (trade_product_column, trade_product_value) = opts.trade_product.to_filter_param(); query_vals.push(trade_product_value); let (media_image_column, media_image_value) = opts.media_image.to_filter_param();