diff options
Diffstat (limited to 'rust/src')
| -rw-r--r-- | rust/src/api_helpers.rs | 21 | ||||
| -rw-r--r-- | rust/src/api_server.rs | 64 | ||||
| -rw-r--r-- | rust/src/api_wrappers.rs | 26 | ||||
| -rw-r--r-- | rust/src/database_models.rs | 5 | ||||
| -rw-r--r-- | rust/src/lib.rs | 26 | 
5 files changed, 79 insertions, 63 deletions
| diff --git a/rust/src/api_helpers.rs b/rust/src/api_helpers.rs index 62fc4569..1ee08c76 100644 --- a/rust/src/api_helpers.rs +++ b/rust/src/api_helpers.rs @@ -1,8 +1,10 @@ +use data_encoding::BASE32_NOPAD;  use database_models::*;  use database_schema::*;  use diesel;  use diesel::prelude::*;  use errors::*; +use uuid::Uuid;  pub fn get_or_create_editgroup(editor_id: i64, conn: &PgConnection) -> Result<i64> {      // check for current active @@ -85,3 +87,22 @@ pub fn accept_editgroup(editgroup_id: i64, conn: &PgConnection) -> Result<Change          Ok(entry)      })  } + +/// Convert fatcat IDs (base32 strings) to UUID +pub fn fcid2uuid(fcid: &str) -> Result<Uuid> { +    if fcid.len() != 26 { +        return Err(ErrorKind::InvalidFatcatId(fcid.to_string()).into()); +    } +    let mut raw = vec![0; 16]; +    BASE32_NOPAD +        .decode_mut(fcid.to_uppercase().as_bytes(), &mut raw) +        .map_err(|_dp| ErrorKind::InvalidFatcatId(fcid.to_string()))?; +    // unwrap() is safe here, because we know raw is always 16 bytes +    Ok(Uuid::from_bytes(&raw).unwrap()) +} + +/// Convert UUID to fatcat ID string (base32 encoded) +pub fn uuid2fcid(id: &Uuid) -> String { +    let raw = id.as_bytes(); +    BASE32_NOPAD.encode(raw).to_lowercase() +} diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs index bd211d1f..b7b19a85 100644 --- a/rust/src/api_server.rs +++ b/rust/src/api_server.rs @@ -1,7 +1,7 @@  //! API endpoint handlers  use ConnectionPool; -use api_helpers::{get_or_create_editgroup, accept_editgroup}; +use api_helpers::{accept_editgroup, get_or_create_editgroup, fcid2uuid, uuid2fcid};  use chrono;  use database_models::*;  use database_schema::{changelog, container_edit, container_ident, container_rev, creator_edit, @@ -40,7 +40,7 @@ macro_rules! entity_history_handler {              limit: Option<i64>,          ) -> Result<Vec<EntityHistoryEntry>> {              let conn = self.db_pool.get().expect("db_pool error"); -            let id = uuid::Uuid::parse_str(&id)?; +            let id = fcid2uuid(&id)?;              let limit = limit.unwrap_or(50);              let rows: Vec<(EditgroupRow, ChangelogRow, $edit_row_type)> = editgroup::table @@ -86,8 +86,8 @@ fn container_row2entity(      let (state, ident_id, redirect_id) = match ident {          Some(i) => (              Some(i.state().unwrap().shortname()), -            Some(i.id.to_string()), -            i.redirect_id.map(|u| u.to_string()), +            Some(uuid2fcid(&i.id)), +            i.redirect_id.map(|u| uuid2fcid(&u)),          ),          None => (None, None, None),      }; @@ -110,8 +110,8 @@ fn creator_row2entity(ident: Option<CreatorIdentRow>, rev: CreatorRevRow) -> Res      let (state, ident_id, redirect_id) = match ident {          Some(i) => (              Some(i.state().unwrap().shortname()), -            Some(i.id.to_string()), -            i.redirect_id.map(|u| u.to_string()), +            Some(uuid2fcid(&i.id)), +            i.redirect_id.map(|u| uuid2fcid(&u)),          ),          None => (None, None, None),      }; @@ -137,8 +137,8 @@ fn file_row2entity(      let (state, ident_id, redirect_id) = match ident {          Some(i) => (              Some(i.state().unwrap().shortname()), -            Some(i.id.to_string()), -            i.redirect_id.map(|u| u.to_string()), +            Some(uuid2fcid(&i.id)), +            i.redirect_id.map(|u| uuid2fcid(&u)),          ),          None => (None, None, None),      }; @@ -147,7 +147,7 @@ fn file_row2entity(          .filter(file_release::file_rev.eq(rev.id))          .get_results(conn)?          .iter() -        .map(|r: &FileReleaseRow| r.target_release_ident_id.to_string()) +        .map(|r: &FileReleaseRow| uuid2fcid(&r.target_release_ident_id))          .collect();      Ok(FileEntity { @@ -175,8 +175,8 @@ fn release_row2entity(      let (state, ident_id, redirect_id) = match ident {          Some(i) => (              Some(i.state().unwrap().shortname()), -            Some(i.id.to_string()), -            i.redirect_id.map(|u| u.to_string()), +            Some(uuid2fcid(&i.id)), +            i.redirect_id.map(|u| uuid2fcid(&u)),          ),          None => (None, None, None),      }; @@ -195,7 +195,7 @@ fn release_row2entity(              year: r.year.clone(),              title: r.title.clone(),              locator: r.locator.clone(), -            target_release_id: r.target_release_ident_id.map(|v| v.to_string()), +            target_release_id: r.target_release_ident_id.map(|v| uuid2fcid(&v)),          })          .collect(); @@ -209,7 +209,7 @@ fn release_row2entity(              index: c.index,              role: c.role.clone(),              raw: c.raw.clone(), -            creator_id: c.creator_ident_id.map(|v| v.to_string()), +            creator_id: c.creator_ident_id.map(|v| uuid2fcid(&v)),          })          .collect(); @@ -224,10 +224,10 @@ fn release_row2entity(          volume: rev.volume,          issue: rev.issue,          pages: rev.pages, -        container_id: rev.container_ident_id.map(|u| u.to_string()), +        container_id: rev.container_ident_id.map(|u| uuid2fcid(&u)),          publisher: rev.publisher,          language: rev.language, -        work_id: Some(rev.work_ident_id.to_string()), +        work_id: Some(uuid2fcid(&rev.work_ident_id)),          refs: Some(refs),          contribs: Some(contribs),          state: state, @@ -243,8 +243,8 @@ fn work_row2entity(ident: Option<WorkIdentRow>, rev: WorkRevRow) -> Result<WorkE      let (state, ident_id, redirect_id) = match ident {          Some(i) => (              Some(i.state().unwrap().shortname()), -            Some(i.id.to_string()), -            i.redirect_id.map(|u| u.to_string()), +            Some(uuid2fcid(&i.id)), +            i.redirect_id.map(|u| uuid2fcid(&u)),          ),          None => (None, None, None),      }; @@ -262,7 +262,7 @@ fn work_row2entity(ident: Option<WorkIdentRow>, rev: WorkRevRow) -> Result<WorkE  impl Server {      pub fn get_container_handler(&self, id: String) -> Result<ContainerEntity> {          let conn = self.db_pool.get().expect("db_pool error"); -        let id = uuid::Uuid::parse_str(&id)?; +        let id = fcid2uuid(&id)?;          // TODO: handle Deletions          let (ident, rev): (ContainerIdentRow, ContainerRevRow) = container_ident::table @@ -288,7 +288,7 @@ impl Server {      pub fn get_creator_handler(&self, id: String) -> Result<CreatorEntity> {          let conn = self.db_pool.get().expect("db_pool error"); -        let id = uuid::Uuid::parse_str(&id)?; +        let id = fcid2uuid(&id)?;          let (ident, rev): (CreatorIdentRow, CreatorRevRow) = creator_ident::table              .find(id) @@ -313,7 +313,7 @@ impl Server {      pub fn get_creator_releases_handler(&self, id: String) -> Result<Vec<ReleaseEntity>> {          let conn = self.db_pool.get().expect("db_pool error"); -        let id = uuid::Uuid::parse_str(&id)?; +        let id = fcid2uuid(&id)?;          // TODO: some kind of unique or group-by?          let rows: Vec<(ReleaseRevRow, ReleaseIdentRow, ReleaseContribRow)> = release_rev::table @@ -331,7 +331,7 @@ impl Server {      pub fn get_file_handler(&self, id: String) -> Result<FileEntity> {          let conn = self.db_pool.get().expect("db_pool error"); -        let id = uuid::Uuid::parse_str(&id)?; +        let id = fcid2uuid(&id)?;          let (ident, rev): (FileIdentRow, FileRevRow) = file_ident::table              .find(id) @@ -356,7 +356,7 @@ impl Server {      pub fn get_release_handler(&self, id: String) -> Result<ReleaseEntity> {          let conn = self.db_pool.get().expect("db_pool error"); -        let id = uuid::Uuid::parse_str(&id)?; +        let id = fcid2uuid(&id)?;          let (ident, rev): (ReleaseIdentRow, ReleaseRevRow) = release_ident::table              .find(id) @@ -381,7 +381,7 @@ impl Server {      pub fn get_release_files_handler(&self, id: String) -> Result<Vec<FileEntity>> {          let conn = self.db_pool.get().expect("db_pool error"); -        let id = uuid::Uuid::parse_str(&id)?; +        let id = fcid2uuid(&id)?;          let rows: Vec<(FileRevRow, FileIdentRow, FileReleaseRow)> = file_rev::table              .inner_join(file_ident::table) @@ -398,7 +398,7 @@ impl Server {      pub fn get_work_handler(&self, id: String) -> Result<WorkEntity> {          let conn = self.db_pool.get().expect("db_pool error"); -        let id = uuid::Uuid::parse_str(&id)?; +        let id = fcid2uuid(&id)?;          let (ident, rev): (WorkIdentRow, WorkRevRow) = work_ident::table              .find(id) @@ -410,7 +410,7 @@ impl Server {      pub fn get_work_releases_handler(&self, id: String) -> Result<Vec<ReleaseEntity>> {          let conn = self.db_pool.get().expect("db_pool error"); -        let id = uuid::Uuid::parse_str(&id)?; +        let id = fcid2uuid(&id)?;          let rows: Vec<(ReleaseRevRow, ReleaseIdentRow)> = release_rev::table              .inner_join(release_ident::table) @@ -559,7 +559,8 @@ impl Server {                          .iter()                          .map(|r| FileReleaseRow {                              file_rev: edit.rev_id.unwrap(), -                            target_release_ident_id: uuid::Uuid::parse_str(r).expect("valid UUID"), +                            target_release_ident_id: fcid2uuid(r) +                                .expect("invalid fatcat identifier"),                          })                          .collect();                      let release_rows: Vec<FileReleaseRow> = insert_into(file_release::table) @@ -595,7 +596,7 @@ impl Server {          };          let work_id = match entity.work_id { -            Some(work_id) => uuid::Uuid::parse_str(&work_id)?, +            Some(work_id) => fcid2uuid(&work_id)?,              None => {                  // If a work_id wasn't passed, create a new work under the current editgroup                  let work_model = models::WorkEntity { @@ -608,12 +609,12 @@ impl Server {                      extra: None,                  };                  let new_entity = self.create_work_handler(work_model, Some(&conn))?; -                uuid::Uuid::parse_str(&new_entity.ident)? +                fcid2uuid(&new_entity.ident)?              }          };          let container_id: Option<uuid::Uuid> = match entity.container_id { -            Some(id) => Some(uuid::Uuid::parse_str(&id)?), +            Some(id) => Some(fcid2uuid(&id)?),              None => None,          }; @@ -657,7 +658,7 @@ impl Server {                              release_rev: edit.rev_id.unwrap(),                              target_release_ident_id: r.target_release_id                                  .clone() -                                .map(|v| uuid::Uuid::parse_str(&v).expect("valid UUID")), +                                .map(|v| fcid2uuid(&v).expect("valid fatcat identifier")),                              index: r.index,                              key: r.key.clone(),                              container_title: r.container_title.clone(), @@ -688,7 +689,7 @@ impl Server {                              release_rev: edit.rev_id.unwrap(),                              creator_ident_id: c.creator_id                                  .clone() -                                .map(|v| uuid::Uuid::parse_str(&v).expect("valid UUID")), +                                .map(|v| fcid2uuid(&v).expect("valid fatcat identifier")),                              index: c.index,                              role: c.role.clone(),                              raw: c.raw.clone(), @@ -1005,4 +1006,3 @@ impl Server {      entity_history_handler!(get_release_history_handler, ReleaseEditRow, release_edit);      entity_history_handler!(get_work_history_handler, WorkEditRow, work_edit);  } - diff --git a/rust/src/api_wrappers.rs b/rust/src/api_wrappers.rs index 8651252c..e10906a8 100644 --- a/rust/src/api_wrappers.rs +++ b/rust/src/api_wrappers.rs @@ -1,7 +1,7 @@  //! API endpoint handlers -use errors::*;  use api_server::Server; +use errors::*;  use fatcat_api::models;  use fatcat_api::models::*;  use fatcat_api::*; @@ -32,6 +32,8 @@ macro_rules! wrap_entity_handlers {                      $get_resp::NotFound(ErrorResponse { message: format!("No such entity {}: {}", stringify!($model), id) }),                  Err(Error(ErrorKind::Uuid(e), _)) =>                      $get_resp::BadRequest(ErrorResponse { message: e.to_string() }), +                Err(Error(ErrorKind::InvalidFatcatId(e), _)) => +                    $get_resp::BadRequest(ErrorResponse { message: e.to_string() }),                  Err(e) => {                      error!("{}", e);                      $get_resp::GenericError(ErrorResponse { message: e.to_string() }) @@ -52,6 +54,8 @@ macro_rules! wrap_entity_handlers {                      $post_resp::BadRequest(ErrorResponse { message: e.to_string() }),                  Err(Error(ErrorKind::Uuid(e), _)) =>                      $post_resp::BadRequest(ErrorResponse { message: e.to_string() }), +                Err(Error(ErrorKind::InvalidFatcatId(e), _)) => +                    $post_resp::BadRequest(ErrorResponse { message: e.to_string() }),                  Err(e) => {                      error!("{}", e);                      $post_resp::GenericError(ErrorResponse { message: e.to_string() }) @@ -72,6 +76,8 @@ macro_rules! wrap_entity_handlers {                      $post_batch_resp::BadRequest(ErrorResponse { message: e.to_string() }),                  Err(Error(ErrorKind::Uuid(e), _)) =>                      $post_batch_resp::BadRequest(ErrorResponse { message: e.to_string() }), +                Err(Error(ErrorKind::InvalidFatcatId(e), _)) => +                    $post_batch_resp::BadRequest(ErrorResponse { message: e.to_string() }),                  Err(e) => {                      error!("{}", e);                      $post_batch_resp::GenericError(ErrorResponse { message: e.to_string() }) @@ -93,6 +99,8 @@ macro_rules! wrap_entity_handlers {                      $get_history_resp::NotFound(ErrorResponse { message: format!("No such entity {}: {}", stringify!($model), id) }),                  Err(Error(ErrorKind::Uuid(e), _)) =>                      $get_history_resp::BadRequest(ErrorResponse { message: e.to_string() }), +                Err(Error(ErrorKind::InvalidFatcatId(e), _)) => +                    $get_history_resp::BadRequest(ErrorResponse { message: e.to_string() }),                  Err(e) => {                      error!("{}", e);                      $get_history_resp::GenericError(ErrorResponse { message: e.to_string() }) @@ -259,17 +267,18 @@ impl Api for Server {          id: i64,          _context: &Context,      ) -> Box<Future<Item = AcceptEditgroupResponse, Error = ApiError> + Send> { -          let ret = match self.accept_editgroup_handler(id) {              Ok(()) => AcceptEditgroupResponse::MergedSuccessfully(Success {                  message: "horray!".to_string(),              }), -            Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => -                AcceptEditgroupResponse::NotFound( -                    ErrorResponse { message: format!("No such editgroup: {}", id) }), -            Err(e) => -                AcceptEditgroupResponse::GenericError( -                    ErrorResponse { message: e.to_string() }), +            Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => { +                AcceptEditgroupResponse::NotFound(ErrorResponse { +                    message: format!("No such editgroup: {}", id), +                }) +            } +            Err(e) => AcceptEditgroupResponse::GenericError(ErrorResponse { +                message: e.to_string(), +            }),          };          Box::new(futures::done(Ok(ret)))      } @@ -298,7 +307,6 @@ impl Api for Server {          entity: models::Editgroup,          _context: &Context,      ) -> Box<Future<Item = CreateEditgroupResponse, Error = ApiError> + Send> { -          let ret = match self.create_editgroup_handler(entity) {              Ok(eg) =>                  CreateEditgroupResponse::SuccessfullyCreated(eg), diff --git a/rust/src/database_models.rs b/rust/src/database_models.rs index 8489b336..15dffad0 100644 --- a/rust/src/database_models.rs +++ b/rust/src/database_models.rs @@ -1,3 +1,4 @@ +use api_helpers::uuid2fcid;  use chrono;  use database_schema::*;  use errors::*; @@ -54,8 +55,8 @@ macro_rules! entity_structs {                  Ok(EntityEdit {                      editgroup_id: self.editgroup_id,                      revision: self.rev_id, -                    redirect_ident: self.redirect_id.map(|v| v.to_string()), -                    ident: self.ident_id.to_string(), +                    redirect_ident: self.redirect_id.map(|v| uuid2fcid(&v)), +                    ident: uuid2fcid(&self.ident_id),                      edit_id: self.id,                      extra: self.extra_json,                  }) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 9de94d86..86e367e4 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -33,7 +33,12 @@ mod errors {                          Uuid(::uuid::ParseError);                          Io(::std::io::Error) #[cfg(unix)];                          Serde(::serde_json::Error); -                        Base32(::data_encoding::DecodeError); +        } +        errors { +            InvalidFatcatId(id: String) { +                description("invalid fatcat identifier syntax") +                display("invalid fatcat identifier (expect 26-char base32 encoded): {}", id) +            }          }      }  } @@ -49,7 +54,6 @@ use dotenv::dotenv;  use iron::middleware::AfterMiddleware;  use iron::{Request, Response};  use std::env; -use data_encoding::BASE32_NOPAD;  #[cfg(feature = "postgres")]  embed_migrations!("../migrations/"); @@ -103,21 +107,3 @@ impl AfterMiddleware for XClacksOverheadMiddleware {          Ok(res)      }  } - -/// Convert fatcat IDs (base32 strings) to UUID -pub fn fcid2uuid(fcid: &str) -> Result<uuid::Uuid> { -    if fcid.len() != 20 { -        bail!("invalid fatcat ID (expecting 20-chars of base32"); -    } -    let mut raw = vec![0; 16]; -    BASE32_NOPAD.decode_mut(fcid.to_uppercase().as_bytes(), &mut raw) -        .map_err(|dp| dp.error)?; -    // unwrap() is safe here, because we know raw is always 16 bytes -    Ok(uuid::Uuid::from_bytes(&raw).unwrap()) -} - -/// Convert UUID to fatcat ID string (base32 encoded) -pub fn uuid2fcid(id: &uuid::Uuid) -> String { -    let raw = id.as_bytes(); -    BASE32_NOPAD.encode(raw).to_lowercase() -} | 
