Cleanup per clippy recommendations

pull/34/head
Mingwei Samuel 2021-10-29 22:38:48 -07:00
parent 0e22623056
commit 0b16c2a385
18 changed files with 58 additions and 56 deletions

View File

@ -32,7 +32,7 @@ fn create_json_response(body: &'static str, status: StatusCode) -> Response<Body
let mut resp = Response::new(Body::from(body)); let mut resp = Response::new(Body::from(body));
*resp.status_mut() = status; *resp.status_mut() = status;
resp.headers_mut().insert(hyper::header::CONTENT_TYPE, HeaderValue::from_static("application/json")); resp.headers_mut().insert(hyper::header::CONTENT_TYPE, HeaderValue::from_static("application/json"));
return resp; resp
} }
/// Main request handler service. /// Main request handler service.
@ -97,7 +97,7 @@ async fn handle_request(req: Request<Body>) -> Result<Response<Body>, Infallible
}; };
*out_response.body_mut() = Body::from((&bytes[..]).to_vec()); *out_response.body_mut() = Body::from((&bytes[..]).to_vec());
} }
return Ok(out_response); Ok(out_response)
} }
/// Gets the region, method_id, and Riot API path based on the given http method and path. /// Gets the region, method_id, and Riot API path based on the given http method and path.
@ -111,7 +111,7 @@ fn parse_path<'a>(http_method: &Method, req_path: &'a str) -> Option<( Route, &'
// Find method_id for given path. // Find method_id for given path.
let method_id = find_matching_method_id(http_method, req_path)?; let method_id = find_matching_method_id(http_method, req_path)?;
return Some(( route, method_id, req_path )) Some(( route, method_id, req_path ))
} }
/// Finds the method_id given the request path. /// Finds the method_id given the request path.

View File

@ -59,7 +59,7 @@ pub fn non_apex_iter() -> Iter {
tier_iter.next(); tier_iter.next();
} }
Iter { Iter {
tier_iter: tier_iter, tier_iter,
div_iter: Division::iter(), div_iter: Division::iter(),
} }
} }

View File

@ -192,11 +192,11 @@ impl num_enum::TryFromPrimitive for Route {
fn try_from_primitive(number: Self::Primitive) -> Result<Self, num_enum::TryFromPrimitiveError<Self>> { fn try_from_primitive(number: Self::Primitive) -> Result<Self, num_enum::TryFromPrimitiveError<Self>> {
RegionalRoute::try_from_primitive(number) RegionalRoute::try_from_primitive(number)
.map(|r| Route::Regional(r)) .map(Route::Regional)
.or_else(|_| PlatformRoute::try_from_primitive(number) .or_else(|_| PlatformRoute::try_from_primitive(number)
.map(|r| Route::Platform(r))) .map(Route::Platform))
.or_else(|_| ValPlatformRoute::try_from_primitive(number) .or_else(|_| ValPlatformRoute::try_from_primitive(number)
.map(|r| Route::ValPlatform(r))) .map(Route::ValPlatform))
.map_err(|_| num_enum::TryFromPrimitiveError { number }) .map_err(|_| num_enum::TryFromPrimitiveError { number })
} }
} }
@ -222,11 +222,11 @@ impl std::str::FromStr for Route {
type Err = strum::ParseError; type Err = strum::ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
RegionalRoute::from_str(s) RegionalRoute::from_str(s)
.map(|r| Self::Regional(r)) .map(Self::Regional)
.or_else(|_| PlatformRoute::from_str(s) .or_else(|_| PlatformRoute::from_str(s)
.map(|r| Self::Platform(r))) .map(Self::Platform))
.or_else(|_| ValPlatformRoute::from_str(s) .or_else(|_| ValPlatformRoute::from_str(s)
.map(|r| Self::ValPlatform(r))) .map(Self::ValPlatform))
.map_err(|_| strum::ParseError::VariantNotFound) .map_err(|_| strum::ParseError::VariantNotFound)
} }
} }
@ -236,11 +236,11 @@ impl Route {
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
let regional = RegionalRoute::iter() let regional = RegionalRoute::iter()
.map(|r| Self::Regional(r)); .map(Self::Regional);
let platform = PlatformRoute::iter() let platform = PlatformRoute::iter()
.map(|r| Self::Platform(r)); .map(Self::Platform);
let val_platform = ValPlatformRoute::iter() let val_platform = ValPlatformRoute::iter()
.map(|r| Self::ValPlatform(r)); .map(Self::ValPlatform);
regional regional
.chain(platform) .chain(platform)

View File

@ -10,6 +10,7 @@
// Version 309704e3979855858e36430b178e507e48702059 // Version 309704e3979855858e36430b178e507e48702059
//! Automatically generated endpoint handles. //! Automatically generated endpoint handles.
#![allow(clippy::let_and_return, clippy::too_many_arguments)]
use crate::models::*; use crate::models::*;

View File

@ -20,10 +20,10 @@ pub struct RiotApiError {
impl RiotApiError { impl RiotApiError {
pub(crate) fn new(reqwest_error: Error, retries: u8, response: Option<Response>, status_code: Option<StatusCode>) -> Self { pub(crate) fn new(reqwest_error: Error, retries: u8, response: Option<Response>, status_code: Option<StatusCode>) -> Self {
Self { Self {
reqwest_error: reqwest_error, reqwest_error,
retries: retries, retries,
response: response, response,
status_code: status_code, status_code,
} }
} }
/// The reqwest::Error for the final failed request. /// The reqwest::Error for the final failed request.

View File

@ -13,7 +13,7 @@
//! //!
//! Note: this modules is automatically generated. //! Note: this modules is automatically generated.
pub static ALL_ENDPOINTS: [(reqwest::Method, &'static str, &'static str); 70] = [ pub static ALL_ENDPOINTS: [(reqwest::Method, &str, &str); 70] = [
(reqwest::Method::GET, "/riot/account/v1/accounts/by-puuid/{puuid}", "account-v1.getByPuuid"), (reqwest::Method::GET, "/riot/account/v1/accounts/by-puuid/{puuid}", "account-v1.getByPuuid"),
(reqwest::Method::GET, "/riot/account/v1/accounts/by-riot-id/{gameName}/{tagLine}", "account-v1.getByRiotId"), (reqwest::Method::GET, "/riot/account/v1/accounts/by-riot-id/{gameName}/{tagLine}", "account-v1.getByRiotId"),
(reqwest::Method::GET, "/riot/account/v1/accounts/me", "account-v1.getByAccessToken"), (reqwest::Method::GET, "/riot/account/v1/accounts/me", "account-v1.getByAccessToken"),

View File

@ -2,7 +2,7 @@ use std::cmp;
use std::time::{ Duration, Instant }; use std::time::{ Duration, Instant };
#[cfg(not(feature="tracing"))] #[cfg(not(feature="tracing"))]
use log; use log as log;
#[cfg(feature="tracing")] #[cfg(feature="tracing")]
use tracing as log; use tracing as log;
@ -45,7 +45,7 @@ impl RateLimit {
let initial_bucket = VectorTokenBucket::new( let initial_bucket = VectorTokenBucket::new(
Duration::from_secs(1), 1, Duration::new(0, 0), 1.0, 1.0); Duration::from_secs(1), 1, Duration::new(0, 0), 1.0, 1.0);
RateLimit { RateLimit {
rate_limit_type: rate_limit_type, rate_limit_type,
// Rate limit before getting from response: 1/s. // Rate limit before getting from response: 1/s.
buckets: RwLock::new(vec![initial_bucket]), buckets: RwLock::new(vec![initial_bucket]),
retry_after: RwLock::new(None), retry_after: RwLock::new(None),
@ -154,7 +154,7 @@ impl RateLimit {
.expect("Failed to parse retry-after header as f32."); .expect("Failed to parse retry-after header as f32.");
// Add 0.5 seconds to account for rounding, cases when response is zero. // Add 0.5 seconds to account for rounding, cases when response is zero.
let delay = Duration::from_secs_f32(0.5 + retry_after_secs); let delay = Duration::from_secs_f32(0.5 + retry_after_secs);
return Some(Instant::now() + delay); Some(Instant::now() + delay)
}() { }() {
*self.retry_after.write() = Some(retry_after); *self.retry_after.write() = Some(retry_after);
} }
@ -188,11 +188,11 @@ impl RateLimit {
} }
} }
fn buckets_require_updating(limit_header: &str, buckets: &Vec<VectorTokenBucket>) -> bool { fn buckets_require_updating(limit_header: &str, buckets: &[VectorTokenBucket]) -> bool {
if buckets.len() != limit_header.split(",").count() { if buckets.len() != limit_header.split(',').count() {
return true; return true;
} }
for (limit_header_entry, bucket) in limit_header.split(",").zip(&*buckets) { for (limit_header_entry, bucket) in limit_header.split(',').zip(buckets) {
// limit_header_entry "100:60" means 100 req per 60 sec. // limit_header_entry "100:60" means 100 req per 60 sec.
let bucket_entry = format!("{}:{}", bucket.get_total_limit(), bucket.get_bucket_duration().as_secs()); let bucket_entry = format!("{}:{}", bucket.get_total_limit(), bucket.get_bucket_duration().as_secs());
if limit_header_entry != bucket_entry { if limit_header_entry != bucket_entry {
@ -205,11 +205,11 @@ fn buckets_require_updating(limit_header: &str, buckets: &Vec<VectorTokenBucket>
fn buckets_from_header(config: &RiotApiConfig, limit_header: &str, count_header: &str, rate_limit_type: RateLimitType) -> Vec<VectorTokenBucket> { fn buckets_from_header(config: &RiotApiConfig, limit_header: &str, count_header: &str, rate_limit_type: RateLimitType) -> Vec<VectorTokenBucket> {
// Limits: "20000:10,1200000:600" // Limits: "20000:10,1200000:600"
// Counts: "7:10,58:600" // Counts: "7:10,58:600"
let size = limit_header.split(",").count(); let size = limit_header.split(',').count();
debug_assert!(size == count_header.split(",").count()); debug_assert!(size == count_header.split(',').count());
let mut out = Vec::with_capacity(size); let mut out = Vec::with_capacity(size);
for (limit_entry, count_entry) in limit_header.split(",").zip(count_header.split(",")) { for (limit_entry, count_entry) in limit_header.split(',').zip(count_header.split(',')) {
let (limit, limit_secs) = scan_fmt!(limit_entry, "{d}:{d}", usize, u64) let (limit, limit_secs) = scan_fmt!(limit_entry, "{d}:{d}", usize, u64)
.unwrap_or_else(|_| panic!("Failed to parse limit entry \"{}\".", limit_entry)); .unwrap_or_else(|_| panic!("Failed to parse limit entry \"{}\".", limit_entry));
let (count, count_secs) = scan_fmt!(count_entry, "{d}:{d}", usize, u64) let (count, count_secs) = scan_fmt!(count_entry, "{d}:{d}", usize, u64)

View File

@ -2,7 +2,7 @@ use std::future::Future;
use std::sync::Arc; use std::sync::Arc;
#[cfg(not(feature="tracing"))] #[cfg(not(feature="tracing"))]
use log; use log as log;
#[cfg(feature="tracing")] #[cfg(feature="tracing")]
use tracing as log; use tracing as log;
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
@ -69,8 +69,8 @@ impl RegionalRequester {
.map_err(|e| RiotApiError::new(e, retries, None, None))?; .map_err(|e| RiotApiError::new(e, retries, None, None))?;
// Maybe update rate limits (based on response headers). // Maybe update rate limits (based on response headers).
self.app_rate_limit.on_response(&config, &response); self.app_rate_limit.on_response(config, &response);
method_rate_limit.on_response(&config, &response); method_rate_limit.on_response(config, &response);
let status = response.status(); let status = response.status();
// Handle normal success / failure cases. // Handle normal success / failure cases.
@ -79,9 +79,9 @@ impl RegionalRequester {
if status.is_success() || status_none { if status.is_success() || status_none {
log::trace!("Response {} (retried {} times), success, returning result.", status, retries); log::trace!("Response {} (retried {} times), success, returning result.", status, retries);
break Ok(ResponseInfo { break Ok(ResponseInfo {
response: response, response,
retries: retries, retries,
status_none: status_none, status_none,
}); });
} }
let err = response.error_for_status_ref().err().unwrap_or_else( let err = response.error_for_status_ref().err().unwrap_or_else(

View File

@ -108,7 +108,7 @@ impl VectorTokenBucket {
while timestamps.back().map_or(false, |ts| *ts < cutoff) { while timestamps.back().map_or(false, |ts| *ts < cutoff) {
timestamps.pop_back(); timestamps.pop_back();
} }
return timestamps; timestamps
} }
} }
@ -159,7 +159,7 @@ impl TokenBucket for VectorTokenBucket {
} }
} }
return true; true
} }
fn get_bucket_duration(&self) -> Duration { fn get_bucket_duration(&self) -> Duration {

View File

@ -2,7 +2,7 @@ use std::future::Future;
use std::sync::Arc; use std::sync::Arc;
#[cfg(not(feature="tracing"))] #[cfg(not(feature="tracing"))]
use log; use log as log;
#[cfg(feature="tracing")] #[cfg(feature="tracing")]
use tracing as log; use tracing as log;
@ -62,7 +62,7 @@ impl RiotApi {
let client_builder = config.client_builder.take() let client_builder = config.client_builder.take()
.expect("CLIENT_BUILDER IN CONFIG SHOULD NOT BE NONE."); .expect("CLIENT_BUILDER IN CONFIG SHOULD NOT BE NONE.");
Self { Self {
config: config, config,
client: client_builder.build().expect("Failed to create client from builder."), client: client_builder.build().expect("Failed to create client from builder."),
regional_requesters: InsertOnlyCHashMap::new(), regional_requesters: InsertOnlyCHashMap::new(),
} }

View File

@ -7,6 +7,7 @@
// Version {{= spec.info.version }} // Version {{= spec.info.version }}
//! Automatically generated endpoint handles. //! Automatically generated endpoint handles.
#![allow(clippy::let_and_return, clippy::too_many_arguments)]
use crate::models::*; use crate::models::*;

View File

@ -18,7 +18,7 @@
//! //!
//! Note: this modules is automatically generated. //! Note: this modules is automatically generated.
pub static ALL_ENDPOINTS: [(reqwest::Method, &'static str, &'static str); {{= operations.length }}] = [ pub static ALL_ENDPOINTS: [(reqwest::Method, &str, &str); {{= operations.length }}] = [
{{ {{
for (const { route, method, operation } of operations) { for (const { route, method, operation } of operations) {
}} }}

View File

@ -12,7 +12,7 @@ use riven::models::tournament_stub_v4::*;
const ROUTE: RegionalRoute = RegionalRoute::AMERICAS; const ROUTE: RegionalRoute = RegionalRoute::AMERICAS;
static MATCHES: [&'static str; 4] = [ "NA1_3923487226", "NA1_4049206905", "NA1_4052515784", "NA1_4062578191" ]; static MATCHES: [&str; 4] = [ "NA1_3923487226", "NA1_4049206905", "NA1_4052515784", "NA1_4062578191" ];
async_tests!{ async_tests!{
my_runner { my_runner {

View File

@ -11,7 +11,7 @@ use riven::consts::*;
const ROUTE: RegionalRoute = RegionalRoute::ASIA; const ROUTE: RegionalRoute = RegionalRoute::ASIA;
static MATCHES: [&'static str; 1] = [ "KR_5495121707" ]; static MATCHES: [&str; 1] = [ "KR_5495121707" ];
async_tests!{ async_tests!{
my_runner { my_runner {

View File

@ -16,16 +16,16 @@ async_tests!{
// Champion Mastery tests. // Champion Mastery tests.
championmastery_getscore_ma5tery: async { championmastery_getscore_ma5tery: async {
let sum = RIOT_API.summoner_v4().get_by_summoner_name(ROUTE, "ma5tery"); let sum = RIOT_API.summoner_v4().get_by_summoner_name(ROUTE, "ma5tery");
let sum = sum.await.map_err(|e| e.to_string())?.ok_or("Failed to get summoner".to_owned())?; let sum = sum.await.map_err(|e| e.to_string())?.ok_or_else(|| "Failed to get summoner".to_owned())?;
let p = RIOT_API.champion_mastery_v4().get_champion_mastery_score(ROUTE, &*sum.id); let p = RIOT_API.champion_mastery_v4().get_champion_mastery_score(ROUTE, &*sum.id);
let s = p.await.map_err(|e| e.to_string())?; let s = p.await.map_err(|e| e.to_string())?;
rassert!(969 <= s && s <= 1000, "Unexpected ma5tery score: {}.", s); rassert!((969..=1000).contains(&s), "Unexpected ma5tery score: {}.", s);
Ok(()) Ok(())
}, },
championmastery_getall_ma5tery: async { championmastery_getall_ma5tery: async {
let sum = RIOT_API.summoner_v4().get_by_summoner_name(ROUTE, "ma5tery"); let sum = RIOT_API.summoner_v4().get_by_summoner_name(ROUTE, "ma5tery");
let sum = sum.await.map_err(|e| e.to_string())?.ok_or("Failed to get summoner".to_owned())?; let sum = sum.await.map_err(|e| e.to_string())?.ok_or_else(|| "Failed to get summoner".to_owned())?;
let p = RIOT_API.champion_mastery_v4().get_all_champion_masteries(ROUTE, &*sum.id); let p = RIOT_API.champion_mastery_v4().get_all_champion_masteries(ROUTE, &*sum.id);
let s = p.await.map_err(|e| e.to_string())?; let s = p.await.map_err(|e| e.to_string())?;
@ -36,11 +36,11 @@ async_tests!{
let featured_p = RIOT_API.spectator_v4().get_featured_games(ROUTE); let featured_p = RIOT_API.spectator_v4().get_featured_games(ROUTE);
let featured = featured_p.await.map_err(|e| e.to_string())?; let featured = featured_p.await.map_err(|e| e.to_string())?;
rassert!(featured.game_list.len() > 0); rassert!(!featured.game_list.is_empty());
let summoner_name = &featured.game_list[0].participants[0].summoner_name; let summoner_name = &featured.game_list[0].participants[0].summoner_name;
let summoner_p = RIOT_API.summoner_v4().get_by_summoner_name(ROUTE, summoner_name); let summoner_p = RIOT_API.summoner_v4().get_by_summoner_name(ROUTE, summoner_name);
let summoner = summoner_p.await.map_err(|e| e.to_string())?.ok_or("Failed to get summoner".to_owned())?; let summoner = summoner_p.await.map_err(|e| e.to_string())?.ok_or_else(|| "Failed to get summoner".to_owned())?;
let livegame_p = RIOT_API.spectator_v4().get_current_game_info_by_summoner(ROUTE, &summoner.id); let livegame_p = RIOT_API.spectator_v4().get_current_game_info_by_summoner(ROUTE, &summoner.id);
let livegame_o = livegame_p.await.map_err(|e| e.to_string())?; let livegame_o = livegame_p.await.map_err(|e| e.to_string())?;

View File

@ -16,7 +16,7 @@ async_tests!{
// Summoner tests. // Summoner tests.
summoner_get_kanjikana: async { summoner_get_kanjikana: async {
let p = RIOT_API.summoner_v4().get_by_summoner_name(ROUTE, "私の 頭が かたい"); let p = RIOT_API.summoner_v4().get_by_summoner_name(ROUTE, "私の 頭が かたい");
let s = p.await.map_err(|e| e.to_string())?.ok_or("Failed to get myheadhard".to_owned())?; let s = p.await.map_err(|e| e.to_string())?.ok_or_else(|| "Failed to get myheadhard".to_owned())?;
rassert_eq!("私の頭がかたい", s.name); rassert_eq!("私の頭がかたい", s.name);
Ok(()) Ok(())
}, },
@ -54,10 +54,10 @@ async_tests!{
// // https://github.com/MingweiSamuel/Riven/issues/25 // // https://github.com/MingweiSamuel/Riven/issues/25
// tft_league_getleagueentriesforsummoner: async { // tft_league_getleagueentriesforsummoner: async {
// let sp = RIOT_API.summoner_v4().get_by_summoner_name(ROUTE, "Caihonbbt"); // let sp = RIOT_API.summoner_v4().get_by_summoner_name(ROUTE, "Caihonbbt");
// let sr = sp.await.map_err(|e| e.to_string())?.ok_or("Failed to get \"Caihonbbt\"".to_owned())?; // let sr = sp.await.map_err(|e| e.to_string())?.ok_or_else(|| "Failed to get \"Caihonbbt\"".to_owned())?;
// let lp = RIOT_API.tft_league_v1().get_league_entries_for_summoner(ROUTE, &sr.id); // let lp = RIOT_API.tft_league_v1().get_league_entries_for_summoner(ROUTE, &sr.id);
// let lr = lp.await.map_err(|e| e.to_string())?; // let lr = lp.await.map_err(|e| e.to_string())?;
// rassert!(0 < lr.len()); // rassert!(!lr.is_empty());
// Ok(()) // Ok(())
// }, // },
// tft-league-v1.getTopRatedLadder // tft-league-v1.getTopRatedLadder
@ -65,7 +65,7 @@ async_tests!{
tft_league_gettopratedladder: async { tft_league_gettopratedladder: async {
let lp = RIOT_API.tft_league_v1().get_top_rated_ladder(ROUTE, QueueType::RANKED_TFT_TURBO); let lp = RIOT_API.tft_league_v1().get_top_rated_ladder(ROUTE, QueueType::RANKED_TFT_TURBO);
let lr = lp.await.map_err(|e| e.to_string())?; let lr = lp.await.map_err(|e| e.to_string())?;
rassert!(0 < lr.len()); rassert!(!lr.is_empty());
Ok(()) Ok(())
}, },
} }

View File

@ -25,8 +25,8 @@ async_tests!{
summoner_double: async { summoner_double: async {
let l1p = RIOT_API.summoner_v4().get_by_summoner_name(ROUTE, "lug nuts k"); let l1p = RIOT_API.summoner_v4().get_by_summoner_name(ROUTE, "lug nuts k");
let l2p = RIOT_API.summoner_v4().get_by_summoner_name(ROUTE, "lugnuts k"); let l2p = RIOT_API.summoner_v4().get_by_summoner_name(ROUTE, "lugnuts k");
let l1 = l1p.await.map_err(|e| e.to_string())?.ok_or("Failed to get l1".to_owned())?; let l1 = l1p.await.map_err(|e| e.to_string())?.ok_or_else(|| "Failed to get l1".to_owned())?;
let l2 = l2p.await.map_err(|e| e.to_string())?.ok_or("Failed to get l2".to_owned())?; let l2 = l2p.await.map_err(|e| e.to_string())?.ok_or_else(|| "Failed to get l2".to_owned())?;
validate_summoners(l1, l2)?; validate_summoners(l1, l2)?;
Ok(()) Ok(())
}, },

View File

@ -14,9 +14,9 @@ lazy_static! {
} }
pub mod ids { pub mod ids {
pub const SUMMONER_ID_LUGNUTSK: &'static str = "SBM8Ubipo4ge2yj7bhEzL7yvV0C9Oc1XA2l6v5okGMA_nCw"; pub const SUMMONER_ID_LUGNUTSK: &str = "SBM8Ubipo4ge2yj7bhEzL7yvV0C9Oc1XA2l6v5okGMA_nCw";
pub const SUMMONER_ID_MA5TERY: &'static str = "IbC4uyFEEW3ZkZw6FZF4bViw3P1EynclAcI6-p-vCpI99Ec"; pub const SUMMONER_ID_MA5TERY: &str = "IbC4uyFEEW3ZkZw6FZF4bViw3P1EynclAcI6-p-vCpI99Ec";
pub const SUMMONER_ID_C9SNEAKY: &'static str = "ghHSdADqgxKwcRl_vWndx6wKiyZx0xKQv-LOhOcU5LU"; pub const SUMMONER_ID_C9SNEAKY: &str = "ghHSdADqgxKwcRl_vWndx6wKiyZx0xKQv-LOhOcU5LU";
pub const ACCOUNT_ID_C9SNEAKY: &'static str = "ML_CcLT94UUHp1iDvXOXCidfmzzPrk_Jbub1f_INhw"; pub const ACCOUNT_ID_C9SNEAKY: &str = "ML_CcLT94UUHp1iDvXOXCidfmzzPrk_Jbub1f_INhw";
pub const ACCOUNT_ID_LUGNUTSK: &'static str = "iheZF2uJ50S84Hfq6Ob8GNlJAUmBmac-EsEEWBJjD01q1jQ"; pub const ACCOUNT_ID_LUGNUTSK: &str = "iheZF2uJ50S84Hfq6Ob8GNlJAUmBmac-EsEEWBJjD01q1jQ";
} }