random ud

This commit is contained in:
2025-08-20 19:19:09 -04:00
commit be481eaa37
4 changed files with 1358 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

1229
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

14
Cargo.toml Normal file
View File

@ -0,0 +1,14 @@
[package]
name = "http_core"
version = "0.1.0"
edition = "2021"
description = "Core traits and types for building API clients with http_derive"
license = "MIT OR Apache-2.0"
[dependencies]
async-trait = "0.1"
awc = "3"
serde = { version = "1", features = ["derive"] }
toml = "0.9.5"
urlencoding = "2"

114
src/lib.rs Normal file
View File

@ -0,0 +1,114 @@
use async_trait::async_trait;
use serde::{ Deserialize, Serialize };
use std::collections::HashMap;
use std::path::Path;
use std::fs;
/// A trait every API request type should implement (via macro).
/// Provides info about the HTTP protocol, URLs, and supported methods.
pub trait HasHttp {
/// Methods this request supports (GET, POST, etc.)
fn http_methods() -> &'static [&'static str];
/// Production endpoint (base URL).
fn live_url() -> &'static str;
/// Sandbox endpoint (base URL).
fn sandbox_url() -> &'static str;
}
/// Trait implemented by all request types that can be sent to an HTTP API.
/// Usually derived via `#[derive(HttpRequest)]`.
#[async_trait]
pub trait Queryable: HasHttp + Send + Sync {
/// The response type for this query.
type R;
/// The error type for this query.
type E: std::error::Error + Send + Sync + 'static;
/// Send the query, with flexibility for environment and overrides.
///
/// - `override_url`: if `Some`, use this instead of live/sandbox.
/// - `sandbox`: if true, use sandbox_url.
/// - `method_override`: optional HTTP method override (GET, POST, etc.).
/// - `headers`: optional headers.
async fn send(
&self,
override_url: Option<&str>,
sandbox: bool,
method_override: Option<&str>,
headers: Option<Vec<(&str, &str)>>,
) -> Result<Self::R, Self::E>;
}
/// Trait for dispatching API calls in bulk or single-shot mode.
///
/// Implemented automatically for generated enums by the `#[api_dispatch]` macro.
#[async_trait]
pub trait ApiDispatch {
/// Send all queries represented by this enum variant.
///
/// - `client`: A preconfigured `awc::Client`
/// - `keys`: A shared API key store (depends on your app crate)
async fn send_all(
&self,
client: &awc::Client,
keys: &HashMap<String, crate::Keys>, // ⚠️ placeholder, you may need to re-export Keys
override_url: Option<&str>,
sandbox: bool,
method_override: Option<&str>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Key {
pub key: String,
pub secret: String,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Keys {
pub keys: HashMap<String, Key>,
}
impl Keys {
/// Convenience method to get a key by name (e.g. "alpaca")
pub fn get(&self, name: &str) -> Option<&Key> {
self.keys.get(name)
}
}
/// Loads API keys from ~/.config/keys or fallback ./keys directory.
/// Each `.toml` file should contain a `Key { key, secret }` struct.
/// The filename (without `.toml`) becomes the key name in the map.
pub fn load_api_keys() -> Result<Keys, Box<dyn std::error::Error>> {
let home_dir = std::env::var("HOME")?;
let config_dir = Path::new(&home_dir).join(".config/keys");
let fallback_dir = Path::new("keys");
let dir_path = if config_dir.exists() {
config_dir
} else {
fallback_dir.to_path_buf()
};
let mut keys_map = HashMap::new();
for entry in fs::read_dir(dir_path)? {
let entry = entry?;
let path = entry.path();
if path.extension().and_then(|s| s.to_str()) == Some("toml") {
let contents = fs::read_to_string(&path)?;
let key: Key = toml::from_str(&contents)?;
if let Some(stem) = path.file_stem().and_then(|s| s.to_str()) {
keys_map.insert(stem.to_string(), key);
}
}
}
Ok(Keys { keys: keys_map })
}