diff options
author | Bryan Newbold <bnewbold@robocracy.org> | 2018-07-20 14:33:09 -0700 |
---|---|---|
committer | Bryan Newbold <bnewbold@robocracy.org> | 2018-07-20 14:33:09 -0700 |
commit | b4eb110bd880f78c5da578fe897ae97d4c734984 (patch) | |
tree | 2eef3aeef360e548680c431abb2b5547f4242632 /rust/src | |
parent | e4c1514294443b9e6f6ed716dcad5ebec64c3af8 (diff) | |
download | fatcat-b4eb110bd880f78c5da578fe897ae97d4c734984.tar.gz fatcat-b4eb110bd880f78c5da578fe897ae97d4c734984.zip |
rust: base32 encoded idents
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() -} |