lib

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

nostr_profile.rs (8079B)


      1 use radroots_replica_db_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_sql_core::error::SqlError;
      9 use radroots_sql_core::{SqlExecutor, utils};
     10 use radroots_types::types::{IError, IResult, IResultList};
     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, IError<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)?.ok_or(IError::from(SqlError::NotFound(id.clone())))?;
     32     Ok(IResult { result })
     33 }
     34 
     35 pub fn find_one(
     36     exec: &dyn SqlExecutor,
     37     opts: &INostrProfileFindOne,
     38 ) -> Result<INostrProfileFindOneResolve, IError<SqlError>> {
     39     let result = match opts {
     40         INostrProfileFindOne::On(args) => find_one_by_on(exec, &args.on)?,
     41         INostrProfileFindOne::Rel(args) => find_one_by_rel(exec, &args.rel)?,
     42     };
     43     Ok(IResult { result })
     44 }
     45 
     46 pub fn find_many(
     47     exec: &dyn SqlExecutor,
     48     opts: &INostrProfileFindMany,
     49 ) -> Result<INostrProfileFindManyResolve, IError<SqlError>> {
     50     let results = match opts {
     51         INostrProfileFindMany::Filter { filter } => find_many_filter(exec, filter)?,
     52         INostrProfileFindMany::Rel { rel } => find_many_by_rel(exec, rel)?,
     53     };
     54     Ok(IResultList { results })
     55 }
     56 
     57 fn find_many_filter(
     58     exec: &dyn SqlExecutor,
     59     filter: &Option<INostrProfileFieldsFilter>,
     60 ) -> Result<Vec<NostrProfile>, IError<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<NostrProfile> = utils::parse_json(&json)?;
     65     Ok(rows)
     66 }
     67 
     68 fn find_one_by_on(
     69     exec: &dyn SqlExecutor,
     70     on: &NostrProfileQueryBindValues,
     71 ) -> Result<Option<NostrProfile>, IError<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<NostrProfile> = utils::parse_json(&json)?;
     77     Ok(rows.pop())
     78 }
     79 
     80 fn rel_query(rel: &NostrProfileFindManyRel) -> (&'static str, Vec<Value>) {
     81     match rel {
     82         NostrProfileFindManyRel::OnRelay(args) => (
     83             "SELECT pr.* FROM nostr_profile pr JOIN nostr_profile_relay pr_rl ON pr.id = pr_rl.tb_pr WHERE pr_rl.tb_rl = ?",
     84             vec![Value::from(args.id.clone())],
     85         ),
     86         NostrProfileFindManyRel::OffRelay(args) => (
     87             "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 = ?)",
     88             vec![Value::from(args.id.clone())],
     89         ),
     90     }
     91 }
     92 
     93 fn find_one_by_rel(
     94     exec: &dyn SqlExecutor,
     95     rel: &NostrProfileFindManyRel,
     96 ) -> Result<Option<NostrProfile>, IError<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<NostrProfile> = utils::parse_json(&json)?;
    102     Ok(rows.pop())
    103 }
    104 
    105 fn find_many_by_rel(
    106     exec: &dyn SqlExecutor,
    107     rel: &NostrProfileFindManyRel,
    108 ) -> Result<Vec<NostrProfile>, IError<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<NostrProfile> = utils::parse_json(&json)?;
    114     Ok(rows)
    115 }
    116 
    117 fn select_by_id(exec: &dyn SqlExecutor, id: &str) -> Result<NostrProfile, IError<SqlError>> {
    118     let params_json =
    119         utils::to_params_json(vec![Value::from(id.to_owned())]).expect("serialize bind params");
    120     let sql = format!("SELECT * FROM {TABLE_NAME} WHERE id = ?;");
    121     let json = exec.query_raw(&sql, &params_json)?;
    122     let mut rows: Vec<NostrProfile> = utils::parse_json(&json)?;
    123     rows.pop()
    124         .ok_or(IError::from(SqlError::NotFound(id.to_owned())))
    125 }
    126 
    127 pub fn update(
    128     exec: &dyn SqlExecutor,
    129     opts: &INostrProfileUpdate,
    130 ) -> Result<INostrProfileUpdateResolve, IError<SqlError>> {
    131     let mut updates =
    132         utils::to_partial_object_map(&opts.fields).expect("serialize partial object map");
    133     if updates.is_empty() {
    134         return Err(IError::from(SqlError::InvalidArgument(String::from(
    135             "no fields to update",
    136         ))));
    137     }
    138     updates.insert(
    139         String::from("updated_at"),
    140         Value::from(utils::time_created_on()),
    141     );
    142     let mut set_parts = Vec::with_capacity(updates.len());
    143     let mut bind_values = Vec::with_capacity(updates.len() + 1);
    144     for (column, value) in updates {
    145         set_parts.push(format!("{column} = ?"));
    146         bind_values.push(utils::to_db_bind_value(&value));
    147     }
    148     let id_for_lookup = match opts.on.primary_key() {
    149         Some(id) => id,
    150         None => {
    151             let found = find_one_by_on(exec, &opts.on)?;
    152             let model = found.ok_or(IError::from(SqlError::NotFound(opts.on.lookup_key())))?;
    153             model.id
    154         }
    155     };
    156     bind_values.push(Value::from(id_for_lookup.clone()));
    157     let sql = format!(
    158         "UPDATE {TABLE_NAME} SET {} WHERE id = ?;",
    159         set_parts.join(", ")
    160     );
    161     let params_json = utils::to_params_json(bind_values).expect("serialize bind params");
    162     let _ = exec.exec(&sql, &params_json)?;
    163     let updated = select_by_id(exec, &id_for_lookup)?;
    164     Ok(IResult { result: updated })
    165 }
    166 
    167 pub fn delete(
    168     exec: &dyn SqlExecutor,
    169     opts: &INostrProfileDelete,
    170 ) -> Result<INostrProfileDeleteResolve, IError<SqlError>> {
    171     let id_for_lookup = match opts {
    172         INostrProfileDelete::On(args) => match args.on.primary_key() {
    173             Some(id) => id,
    174             None => {
    175                 let found = find_one_by_on(exec, &args.on)?;
    176                 let model = found.ok_or(IError::from(SqlError::NotFound(args.on.lookup_key())))?;
    177                 model.id
    178             }
    179         },
    180         INostrProfileDelete::Rel(args) => {
    181             let found = find_one_by_rel(exec, &args.rel)?;
    182             let model = found.ok_or(IError::from(SqlError::NotFound(rel_lookup_key(&args.rel))))?;
    183             model.id
    184         }
    185     };
    186     let params_json = utils::to_params_json(vec![Value::from(id_for_lookup.clone())])
    187         .expect("serialize bind params");
    188     let sql = format!("DELETE FROM {TABLE_NAME} WHERE id = ?;");
    189     let outcome = exec.exec(&sql, &params_json)?;
    190     if outcome.changes == 0 {
    191         return Err(IError::from(SqlError::NotFound(id_for_lookup.clone())));
    192     }
    193     Ok(IResult {
    194         result: id_for_lookup,
    195     })
    196 }
    197 
    198 fn rel_lookup_key(rel: &NostrProfileFindManyRel) -> String {
    199     match rel {
    200         NostrProfileFindManyRel::OnRelay(args) => format!("on_relay:{}", args.id.as_str()),
    201         NostrProfileFindManyRel::OffRelay(args) => format!("off_relay:{}", args.id.as_str()),
    202     }
    203 }