lib

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

nostr_relay.rs (8574B)


      1 use radroots_replica_schema::nostr_relay::{
      2     INostrRelayCreate, INostrRelayCreateResolve, INostrRelayDelete, INostrRelayDeleteResolve,
      3     INostrRelayFieldsFilter, INostrRelayFindMany, INostrRelayFindManyResolve, INostrRelayFindOne,
      4     INostrRelayFindOneResolve, INostrRelayUpdate, INostrRelayUpdateResolve, NostrRelay,
      5     NostrRelayFindManyRel, NostrRelayQueryBindValues,
      6 };
      7 use radroots_replica_schema::{ReplicaSchemaError, ReplicaSchemaResult, ReplicaSchemaResultList};
      8 use radroots_sql_core::error::SqlError;
      9 use radroots_sql_core::{SqlExecutor, utils};
     10 use serde_json::Value;
     11 
     12 const TABLE_NAME: &str = "nostr_relay";
     13 
     14 pub fn create(
     15     exec: &dyn SqlExecutor,
     16     opts: &INostrRelayCreate,
     17 ) -> Result<INostrRelayCreateResolve, ReplicaSchemaError<SqlError>> {
     18     let field_map = utils::to_object_map(opts).expect("serialize object map");
     19     let id = utils::uuidv4();
     20     let now = utils::time_created_on();
     21     let meta: [(&str, Value); 3] = [
     22         ("id", Value::from(id.clone())),
     23         ("created_at", Value::from(now.clone())),
     24         ("updated_at", Value::from(now.clone())),
     25     ];
     26     let (sql, bind_values) = utils::build_insert_query_with_meta(TABLE_NAME, &meta, &field_map);
     27     let params_json = utils::to_params_json(bind_values).expect("serialize bind params");
     28     let _ = exec.exec(&sql, &params_json)?;
     29     let on = NostrRelayQueryBindValues::Id { id: id.clone() };
     30     let result = find_one_by_on(exec, &on)?
     31         .ok_or(ReplicaSchemaError::from(SqlError::NotFound(id.clone())))?;
     32     Ok(ReplicaSchemaResult { result })
     33 }
     34 
     35 pub fn find_one(
     36     exec: &dyn SqlExecutor,
     37     opts: &INostrRelayFindOne,
     38 ) -> Result<INostrRelayFindOneResolve, ReplicaSchemaError<SqlError>> {
     39     let result = match opts {
     40         INostrRelayFindOne::On(args) => find_one_by_on(exec, &args.on)?,
     41         INostrRelayFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?,
     42     };
     43     Ok(ReplicaSchemaResult { result })
     44 }
     45 
     46 pub fn find_many(
     47     exec: &dyn SqlExecutor,
     48     opts: &INostrRelayFindMany,
     49 ) -> Result<INostrRelayFindManyResolve, ReplicaSchemaError<SqlError>> {
     50     let results = match opts {
     51         INostrRelayFindMany::Filter { filter } => find_many_filter(exec, filter)?,
     52         INostrRelayFindMany::Rel { rel } => find_many_by_rel(exec, rel)?,
     53     };
     54     Ok(ReplicaSchemaResultList { results })
     55 }
     56 
     57 fn find_many_filter(
     58     exec: &dyn SqlExecutor,
     59     filter: &Option<INostrRelayFieldsFilter>,
     60 ) -> Result<Vec<NostrRelay>, ReplicaSchemaError<SqlError>> {
     61     let (sql, bind_values) = utils::build_select_query_with_meta(TABLE_NAME, filter.as_ref());
     62     let params_json = utils::to_params_json(bind_values).expect("serialize bind params");
     63     let json = exec.query_raw(&sql, &params_json)?;
     64     let rows: Vec<NostrRelay> = utils::parse_json(&json)?;
     65     Ok(rows)
     66 }
     67 
     68 fn find_one_by_on(
     69     exec: &dyn SqlExecutor,
     70     on: &NostrRelayQueryBindValues,
     71 ) -> Result<Option<NostrRelay>, ReplicaSchemaError<SqlError>> {
     72     let (column, value) = on.to_filter_param();
     73     let sql = format!("SELECT * FROM {TABLE_NAME} WHERE {column} = ? LIMIT 1;");
     74     let params_json = utils::to_params_json(vec![value]).expect("serialize bind params");
     75     let json = exec.query_raw(&sql, &params_json)?;
     76     let mut rows: Vec<NostrRelay> = utils::parse_json(&json)?;
     77     Ok(rows.pop())
     78 }
     79 
     80 fn rel_query(rel: &NostrRelayFindManyRel) -> (&'static str, Vec<Value>) {
     81     match rel {
     82         NostrRelayFindManyRel::OnProfile(args) => (
     83             "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 = ?",
     84             vec![Value::from(args.public_key.clone())],
     85         ),
     86         NostrRelayFindManyRel::OffProfile(args) => (
     87             "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 <> ?",
     88             vec![Value::from(args.public_key.clone())],
     89         ),
     90     }
     91 }
     92 
     93 fn find_one_by_rel(
     94     exec: &dyn SqlExecutor,
     95     rel: &NostrRelayFindManyRel,
     96 ) -> Result<Option<NostrRelay>, ReplicaSchemaError<SqlError>> {
     97     let (sql, bind_values) = rel_query(rel);
     98     let params_json = utils::to_params_json(bind_values).expect("serialize bind params");
     99     let sql = format!("{sql} LIMIT 1;");
    100     let json = exec.query_raw(&sql, &params_json)?;
    101     let mut rows: Vec<NostrRelay> = utils::parse_json(&json)?;
    102     Ok(rows.pop())
    103 }
    104 
    105 fn find_many_by_rel(
    106     exec: &dyn SqlExecutor,
    107     rel: &NostrRelayFindManyRel,
    108 ) -> Result<Vec<NostrRelay>, ReplicaSchemaError<SqlError>> {
    109     let (sql, bind_values) = rel_query(rel);
    110     let params_json = utils::to_params_json(bind_values).expect("serialize bind params");
    111     let sql = format!("{sql};");
    112     let json = exec.query_raw(&sql, &params_json)?;
    113     let rows: Vec<NostrRelay> = utils::parse_json(&json)?;
    114     Ok(rows)
    115 }
    116 
    117 fn select_by_id(
    118     exec: &dyn SqlExecutor,
    119     id: &str,
    120 ) -> Result<NostrRelay, ReplicaSchemaError<SqlError>> {
    121     let params_json =
    122         utils::to_params_json(vec![Value::from(id.to_owned())]).expect("serialize bind params");
    123     let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;");
    124     let json = exec.query_raw(&sql, &params_json)?;
    125     let mut rows: Vec<NostrRelay> = utils::parse_json(&json)?;
    126     rows.pop()
    127         .ok_or(ReplicaSchemaError::from(SqlError::NotFound(id.to_owned())))
    128 }
    129 
    130 pub fn update(
    131     exec: &dyn SqlExecutor,
    132     opts: &INostrRelayUpdate,
    133 ) -> Result<INostrRelayUpdateResolve, ReplicaSchemaError<SqlError>> {
    134     let mut updates =
    135         utils::to_partial_object_map(&opts.fields).expect("serialize partial object map");
    136     if updates.is_empty() {
    137         return Err(ReplicaSchemaError::from(SqlError::InvalidArgument(
    138             String::from("no fields to update"),
    139         )));
    140     }
    141     updates.insert(
    142         String::from("updated_at"),
    143         Value::from(utils::time_created_on()),
    144     );
    145     let mut set_parts = Vec::with_capacity(updates.len());
    146     let mut bind_values = Vec::with_capacity(updates.len() + 1);
    147     for (column, value) in updates {
    148         set_parts.push(format!("{column} = ?"));
    149         bind_values.push(utils::to_db_bind_value(&value));
    150     }
    151     let id_for_lookup = match opts.on.primary_key() {
    152         Some(id) => id,
    153         None => {
    154             let found = find_one_by_on(exec, &opts.on)?;
    155             let model = found.ok_or(ReplicaSchemaError::from(SqlError::NotFound(
    156                 opts.on.lookup_key(),
    157             )))?;
    158             model.id
    159         }
    160     };
    161     bind_values.push(Value::from(id_for_lookup.clone()));
    162     let sql = format!(
    163         "UPDATE {TABLE_NAME} SET {} WHERE id = ?;",
    164         set_parts.join(", ")
    165     );
    166     let params_json = utils::to_params_json(bind_values).expect("serialize bind params");
    167     let _ = exec.exec(&sql, &params_json)?;
    168     let updated = select_by_id(exec, &id_for_lookup)?;
    169     Ok(ReplicaSchemaResult { result: updated })
    170 }
    171 
    172 pub fn delete(
    173     exec: &dyn SqlExecutor,
    174     opts: &INostrRelayDelete,
    175 ) -> Result<INostrRelayDeleteResolve, ReplicaSchemaError<SqlError>> {
    176     let id_for_lookup = match opts {
    177         INostrRelayDelete::On(args) => match args.on.primary_key() {
    178             Some(id) => id,
    179             None => {
    180                 let found = find_one_by_on(exec, &args.on)?;
    181                 let model = found.ok_or(ReplicaSchemaError::from(SqlError::NotFound(
    182                     args.on.lookup_key(),
    183                 )))?;
    184                 model.id
    185             }
    186         },
    187         INostrRelayDelete::Rel(args) => {
    188             let found = find_one_by_rel(exec, &args.rel)?;
    189             let model = found.ok_or(ReplicaSchemaError::from(SqlError::NotFound(
    190                 rel_lookup_key(&args.rel),
    191             )))?;
    192             model.id
    193         }
    194     };
    195     let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])
    196         .expect("serialize bind params");
    197     let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;");
    198     let outcome = exec.exec(&sql, &params_json)?;
    199     if outcome.changes == 0 {
    200         return Err(ReplicaSchemaError::from(SqlError::NotFound(
    201             id_for_lookup.clone(),
    202         )));
    203     }
    204     Ok(ReplicaSchemaResult {
    205         result: id_for_lookup,
    206     })
    207 }
    208 
    209 fn rel_lookup_key(rel: &NostrRelayFindManyRel) -> String {
    210     match rel {
    211         NostrRelayFindManyRel::OnProfile(args) => {
    212             format!("on_profile:{}", args.public_key.as_str())
    213         }
    214         NostrRelayFindManyRel::OffProfile(args) => {
    215             format!("off_profile:{}", args.public_key.as_str())
    216         }
    217     }
    218 }