110 lines
2.6 KiB
Rust
110 lines
2.6 KiB
Rust
// During development, allowing dead code
|
|
#![allow(dead_code)]
|
|
|
|
use async_recursion::async_recursion;
|
|
use clap::Parser;
|
|
use std::fmt::Write;
|
|
use std::error::Error;
|
|
// use std::time::Duration;
|
|
// use std::thread::sleep;
|
|
use serde::Deserialize;
|
|
|
|
type DataResult<T> = Result<T, Box<dyn Error>>;
|
|
|
|
const BASE_URL: &str = "https://api.semanticscholar.org/graph/v1";
|
|
const MAX_DEPTH: u32 = 3;
|
|
|
|
#[derive(Parser, Debug)]
|
|
#[clap(author, version, about, long_about = None)]
|
|
struct Args {
|
|
/// URL to query
|
|
#[clap(short, long, value_parser)]
|
|
paper_id: String,
|
|
}
|
|
|
|
struct Author {
|
|
name: String
|
|
}
|
|
struct Paper {
|
|
authors: Vec<Author>
|
|
}
|
|
|
|
/**
|
|
* Occurs within Citation struct
|
|
*/
|
|
#[derive(Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
struct CitingPaper {
|
|
paper_id: String,
|
|
title: String,
|
|
}
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
struct Citation {
|
|
citing_paper: CitingPaper
|
|
}
|
|
|
|
/**
|
|
* Generic struct to wrap the common API response pattern {data: [...]}
|
|
*/
|
|
#[derive(Deserialize, Debug)]
|
|
struct ApiListResponse<T> {
|
|
data: Vec<T>
|
|
}
|
|
|
|
|
|
// TODO: Cache results in a (separate but local) database such as Redis
|
|
// TODO: Store results in a (separate but local) database such as Postgres
|
|
#[async_recursion]
|
|
async fn get_citations(paper_id: String, depth: u32) -> DataResult<Vec<Citation>> {
|
|
// Bound recursion to some depth
|
|
if depth > MAX_DEPTH {
|
|
return Ok(vec![]);
|
|
}
|
|
|
|
// Naieve approach to possible rate-limiting
|
|
// sleep(Duration::new(1, 0));
|
|
|
|
// Build the URL
|
|
let mut url = String::new();
|
|
write!(&mut url, "{}/paper/{}/citations", BASE_URL, paper_id)?;
|
|
|
|
let resp = reqwest::get(url)
|
|
.await?
|
|
.text()
|
|
.await?;
|
|
// .json::<serde_json::Value>().await?;
|
|
|
|
let resp_deserialized_attempt = serde_json::from_str::<ApiListResponse<Citation>>(resp.as_str());
|
|
|
|
if let Err(err) = resp_deserialized_attempt {
|
|
println!("depth {} paper {} error {}", depth, paper_id, err);
|
|
return Ok(vec![]);
|
|
}
|
|
|
|
let resp_deserialized: ApiListResponse<Citation> = resp_deserialized_attempt.unwrap();
|
|
|
|
for Citation{citing_paper: CitingPaper{paper_id: cited_id, title}} in resp_deserialized.data {
|
|
println!("depth {} paper {} cites {} title {}", depth, paper_id, cited_id, title);
|
|
|
|
get_citations(cited_id, depth + 1).await?;
|
|
}
|
|
|
|
Ok(vec![])
|
|
}
|
|
|
|
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn Error>> {
|
|
let Args{ paper_id } = Args::parse();
|
|
|
|
// let mut authors: Vec<Author> = Vec::new();
|
|
|
|
// let citations =
|
|
|
|
get_citations(paper_id, 0).await?;
|
|
|
|
Ok(())
|
|
} |