Skip to content
This repository has been archived by the owner on Aug 1, 2022. It is now read-only.

Commit

Permalink
Extend schema with avatar fallback (#218)
Browse files Browse the repository at this point in the history
* Implement avatar query
Closes #213 

* Implement Display to avoid internal leak
* Extend Identity with avatarFallback
* Please le clippy
  • Loading branch information
xla committed Mar 12, 2020
1 parent aa88f62 commit 0593af7
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 7 deletions.
28 changes: 21 additions & 7 deletions proxy/src/avatar.rs
@@ -1,8 +1,13 @@
#![allow(clippy::as_conversions, clippy::float_arithmetic)]

//! Org and user avatar generation.

use std::fmt;

/// Emoji whitelist.
///
/// Note that these are `str` and not `char` because an emoji can span multiple unicode scalars.
#[allow(clippy::non_ascii_literal)]
const EMOJIS: &[&str] = &[
"⌚️", "📱", "📲", "💻", "⌨️", "💽", "💾", "💿", "📀", "📼", "📷", "📸", "📹", "🎥", "🎞", "📞",
"☎️", "📟", "📠", "📺", "📻", "⏰", "🕰", "⌛️", "⏳", "📡", "🔋", "🔌", "💡", "🔦", "💸", "💵",
Expand All @@ -21,6 +26,12 @@ const EMOJIS: &[&str] = &[
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Emoji(&'static str);

impl fmt::Display for Emoji {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}

/// An avatar.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Avatar {
Expand All @@ -32,6 +43,7 @@ pub struct Avatar {

impl Avatar {
/// Generate an avatar from an input string.
#[must_use]
pub fn from(input: &str) -> Self {
Self {
emoji: generate_emoji(input),
Expand All @@ -50,18 +62,20 @@ pub struct Color {
/// The blue channel.
pub b: u8,

// The alpha is here to facilitate working with `u32` values.
// We don't use it as part of the output.
/// The alpha is here to facilitate working with `u32` values.
/// We don't use it as part of the output.
a: u8,
}

impl Color {
/// Create a new color from individual channels.
pub fn new(r: u8, g: u8, b: u8) -> Self {
#[must_use]
pub const fn new(r: u8, g: u8, b: u8) -> Self {
Self { r, g, b, a: 0x0 }
}

/// Compute the lightness of a color.
#[must_use]
pub fn lightness(&self) -> f32 {
let r = self.r as f32;
let g = self.g as f32;
Expand Down Expand Up @@ -134,11 +148,11 @@ fn compress_color(c: Color) -> Color {
/// platforms.
fn hash(input: &str) -> u64 {
let bytes = input.bytes();
let mut hash: u64 = 0xcbf29ce484222325; // FNV offset basis.
let mut hash: u64 = 0xcbf2_9ce4_8422_2325; // FNV offset basis.

for byte in bytes {
hash = hash ^ (byte as u64);
hash = hash.wrapping_mul(0x100000001b3);
hash ^= u64::from(byte);
hash = hash.wrapping_mul(0x100_0000_01b3);
}

hash
Expand All @@ -161,7 +175,7 @@ mod test {

#[test]
fn test_avatar_hash() {
assert_eq!(hash("chongo was here!\n\0"), 0xc33bce57bef63eaf);
assert_eq!(hash("chongo was here!\n\0"), 0xc33b_ce57_bef6_3eaf);
}

#[test]
Expand Down
35 changes: 35 additions & 0 deletions proxy/src/graphql/schema.rs
Expand Up @@ -8,6 +8,7 @@ use librad::surf;
use librad::surf::git::git2;
use radicle_registry_client::ed25519;

use crate::avatar;
use crate::coco;
use crate::error;
use crate::identity;
Expand Down Expand Up @@ -149,6 +150,10 @@ impl Query {
"1.0"
}

fn avatar(handle: juniper::ID) -> Result<avatar::Avatar, error::Error> {
Ok(avatar::Avatar::from(&handle.to_string()))
}

fn blob(
ctx: &Context,
id: juniper::ID,
Expand Down Expand Up @@ -359,6 +364,32 @@ pub struct ControlQuery;
)]
impl ControlQuery {}

#[juniper::object]
impl avatar::Avatar {
fn background(&self) -> avatar::Color {
self.background
}

fn emoji(&self) -> String {
self.emoji.to_string()
}
}

#[juniper::object]
impl avatar::Color {
fn r() -> i32 {
i32::from(self.r)
}

fn g() -> i32 {
i32::from(self.g)
}

fn b() -> i32 {
i32::from(self.b)
}
}

#[juniper::object]
impl coco::Blob {
fn binary(&self) -> bool {
Expand Down Expand Up @@ -486,6 +517,10 @@ impl identity::Identity {
fn metadata(&self) -> &identity::Metadata {
&self.metadata
}

fn avatar_fallback(&self) -> avatar::Avatar {
avatar::Avatar::from(&self.metadata.handle)
}
}

#[juniper::object(name = "IdentityMetadata")]
Expand Down
53 changes: 53 additions & 0 deletions proxy/tests/graphql_query.rs
Expand Up @@ -21,6 +21,43 @@ fn api_version() {
});
}

#[test]
fn avatar() {
with_fixtures(|librad_paths, _repos_dir, _platinum_id| {
let mut vars = Variables::new();

vars.insert("handle".into(), InputValue::scalar("cloudhead"));

let query = "query($handle: ID!) {
avatar(handle: $handle) {
emoji
background {
r
g
b
}
}
}";

execute_query(librad_paths, query, &vars, |res, errors| {
assert_eq!(errors, []);
assert_eq!(
res,
graphql_value!({
"avatar": {
"emoji": "🧱",
"background": {
"r": 24,
"g": 105,
"b": 216,
},
}
})
);
});
})
}

#[test]
fn blob() {
with_fixtures(|librad_paths, _repos_dir, platinum_id| {
Expand Down Expand Up @@ -556,6 +593,14 @@ fn identity() {
displayName
avatarUrl
}
avatarFallback {
emoji
background {
r
g
b
}
}
}
}";

Expand All @@ -572,6 +617,14 @@ fn identity() {
"displayName": "Alexis Sellier",
"avatarUrl": "https://avatars1.githubusercontent.com/u/4077",
},
"avatarFallback": {
"emoji": "🧱",
"background": {
"r": 24,
"g": 105,
"b": 216,
},
}
},
})
);
Expand Down

0 comments on commit 0593af7

Please sign in to comment.