lib

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

nostr_profile.rs (8522B)


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