Implement display of identifiers

- Implement identifier types and display
- Move all of it into its own submodule
- Parse CLI args as the list of identifiers to fetch
This commit is contained in:
Amelia 2023-11-01 14:11:59 +01:00
parent 1dac97b2dc
commit 6d2b3ead16
Signed by: limefox
GPG key ID: F86ACA6D693E7BE9
4 changed files with 220 additions and 51 deletions

View file

@ -11,3 +11,6 @@ license = "ACSL"
reqwest = { version = "0.11.22", features = ["default-tls", "json", "gzip"] } reqwest = { version = "0.11.22", features = ["default-tls", "json", "gzip"] }
serde = { version = "1.0.190", features = ["derive"] } serde = { version = "1.0.190", features = ["derive"] }
tokio = { version = "1.33.0", features = ["rt-multi-thread", "macros", "net"] } tokio = { version = "1.33.0", features = ["rt-multi-thread", "macros", "net"] }
[[bin]]
name = "estrus"

4
README.md Normal file
View file

@ -0,0 +1,4 @@
Estrus Elixir Navigator
===
CLI navigator for the Elixir linux project.

182
src/ident.rs Normal file
View file

@ -0,0 +1,182 @@
//! Structures structuring identifiers
//!
use std::fmt::{self, Display};
use std::iter::FromIterator;
use std::marker::PhantomData as Phantom;
use std::path::PathBuf;
use std::str::FromStr;
use serde::de::{self, Deserializer, Visitor};
use reqwest::Client;
/// Response to an query on the Elixir API
///
/// Holds the deserialized response to a query to the elixir API regarding definitions, references,
/// and documentation of an identifier.
#[derive(serde::Deserialize, Debug, Default)]
pub struct Response {
/// List of definitions
definitions: Vec<Definition>,
/// List of references
references: Vec<Reference>,
/// List of positions in the documentation
documentations: Vec<Documentation>,
}
impl Response {
/// Retrieve definitions of the identifier
pub fn get_definitions(&self) -> &[Definition] {
&self.definitions
}
/// Retrieve references of the identifier
pub fn get_references(&self) -> &[Reference] {
&self.references
}
/// Retrieve documentation for the identifier
pub fn get_documentations(&self) -> &[Documentation] {
&self.documentations
}
}
#[derive(serde::Deserialize, Debug)]
/// Different possible types of ifier Definitions
pub enum DefinitionType {
#[serde(rename(deserialize = "prototype"))]
Prototype,
#[serde(rename(deserialize = "member"))]
Member,
#[serde(rename(deserialize = "function"))]
Function,
#[serde(rename(deserialize = "enumerator"))]
Enumerator,
#[serde(rename(deserialize = "struct"))]
Struct,
#[serde(rename(deserialize = "variable"))]
Variable,
}
impl std::fmt::Display for DefinitionType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::fmt::Display for Definition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"[{:^9}] {}:{}",
self.deftype.to_string(),
self.path.to_str().unwrap_or("<???>"),
self.line
)
}
}
#[derive(serde::Deserialize, Debug)]
pub struct Definition {
path: PathBuf,
line: u32,
#[serde(rename(deserialize = "type"))]
deftype: DefinitionType,
}
fn comma_separated<'de, V, T, D>(deserializer: D) -> Result<V, D::Error>
where
V: FromIterator<T>,
T: FromStr,
T::Err: Display,
D: Deserializer<'de>,
{
struct CommaSeparated<V, T>(Phantom<V>, Phantom<T>);
impl<'de, V, T> Visitor<'de> for CommaSeparated<V, T>
where
V: FromIterator<T>,
T: FromStr,
T::Err: Display,
{
type Value = V;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("string containing comma-separated elements")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
s.split(',')
.map(FromStr::from_str)
.collect::<Result<Self::Value, _>>()
.map_err(de::Error::custom)
}
}
let visitor = CommaSeparated(Phantom, Phantom);
deserializer.deserialize_str(visitor)
}
#[derive(serde::Deserialize, Debug)]
pub struct Reference {
path: PathBuf,
#[serde(deserialize_with = "comma_separated")]
line: Vec<u32>,
//#[serde(rename(serialize = "type"))]
//reftype: Option<()>,
}
impl std::fmt::Display for Reference {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}:{}",
self.path.to_str().unwrap_or("<???>"),
self.line
.iter()
.map(u32::to_string)
.collect::<Vec<String>>()
.join(",")
)
}
}
#[derive(serde::Deserialize, Debug)]
pub struct Documentation {
path: PathBuf,
#[serde(deserialize_with = "comma_separated")]
line: Vec<u32>,
//#[serde(rename(serialize = "type"))]
//doctype: Option<()>,
}
impl std::fmt::Display for Documentation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}:{}",
self.path.to_str().unwrap_or("<???>"),
self.line
.iter()
.map(u32::to_string)
.collect::<Vec<String>>()
.join(",")
)
}
}
pub async fn find(ident: &str) -> Response {
let client = Client::new();
let res = client
.get(format!(
"https://elixir.bootlin.com/api/ident/linux/{ident}?version=latest"
))
.send()
.await
.expect("No future failure");
res.json::<Response>().await.expect("No failure")
}

View file

@ -1,60 +1,40 @@
//! Main module for the project //! Main module for the project
//! //!
use std::path::PathBuf; #![deny(clippy::perf)]
#![deny(clippy::pedantic)]
#![deny(clippy::complexity)]
#![deny(clippy::correctness)]
use reqwest::Client; use std::env::args;
/// Response to an Ident query on the Elixir API mod ident;
///
/// Holds the deserialized response to a query to the elixir API regarding definitions, references,
/// and documentation of an identifier.
#[derive(serde::Deserialize, Debug)]
struct IdentResponse {
/// List of definitions
definitions: Vec<IdentDefinition>,
/// List of references
references: Vec<IdentReference>,
/// List of positions in the documentation
documentations: Vec<IdentDocumentation>,
}
#[derive(serde::Deserialize, Debug)]
/// Different possible types of Identifier Definitions
enum IdentDefinitionType {
Prototype,
}
#[derive(serde::Deserialize, Debug)]
struct IdentDefinition {
path: PathBuf,
line: u32,
deftype: IdentDefinitionType,
}
#[derive(serde::Deserialize, Debug)]
struct IdentReference {
path: PathBuf,
line: u32,
reftype: Option<()>
}
#[derive(serde::Deserialize, Debug)]
struct IdentDocumentation {
path: PathBuf,
line: u32,
doctype: Option<()>
}
async fn find_ident(ident: &str) -> IdentResponse {
let client = Client::new();
let res = client.get(format!("https://elixir.bootlin.com/api/ident/linux/{ident}?version=latest"))
.send()
.await.expect("No future failure");
res.json::<IdentResponse>().await.expect("No failure")
}
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
println!("{:?}", find_ident("clear_page_erms").await) let arg_list = args().skip(1);
for arg in arg_list {
println!("{:=^60}", format!(" {arg} "));
show_one_ident(&arg).await;
std::thread::sleep(std::time::Duration::from_millis(1000));
println!("")
}
}
async fn show_one_ident(ident: &str) {
let id_resp = ident::find(ident).await;
println!("Definitions:");
for defs in id_resp.get_definitions() {
println!(" - {defs}");
}
println!("References:");
for refs in id_resp.get_references() {
println!(" - {refs}");
}
println!("Documentation:");
for docs in id_resp.get_documentations() {
println!(" - {docs}");
}
} }