aboutsummaryrefslogtreecommitdiffstats
path: root/rust/src
diff options
context:
space:
mode:
authorBryan Newbold <bnewbold@robocracy.org>2018-07-20 14:33:09 -0700
committerBryan Newbold <bnewbold@robocracy.org>2018-07-20 14:33:09 -0700
commitb4eb110bd880f78c5da578fe897ae97d4c734984 (patch)
tree2eef3aeef360e548680c431abb2b5547f4242632 /rust/src
parente4c1514294443b9e6f6ed716dcad5ebec64c3af8 (diff)
downloadfatcat-b4eb110bd880f78c5da578fe897ae97d4c734984.tar.gz
fatcat-b4eb110bd880f78c5da578fe897ae97d4c734984.zip
rust: base32 encoded idents
Diffstat (limited to 'rust/src')
-rw-r--r--rust/src/api_helpers.rs21
-rw-r--r--rust/src/api_server.rs64
-rw-r--r--rust/src/api_wrappers.rs26
-rw-r--r--rust/src/database_models.rs5
-rw-r--r--rust/src/lib.rs26
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()
-}