use awc::Client;
use case_insensitive_string::CaseInsensitiveString;
use libsubconverter::upload::gist::upload_gist;
use serde_json::Value;
use std::collections::HashMap;
use std::env;
use std::fs;
use std::io::Write;
use std::time::Duration;
const DUMMY_INI_PATH: &str = "./gistconf.ini";
async fn fetch_first_gist_details(token: &str) -> Option<(String, String)> {
log::info!("Attempting to fetch existing Gist details using provided token...");
let client = Client::builder().timeout(Duration::from_secs(10)).finish();
let url = "https://api.github.com/gists";
let mut headers = HashMap::new();
headers.insert(
CaseInsensitiveString::new("Authorization"),
format!("token {}", token),
);
headers.insert(
CaseInsensitiveString::new("Accept"),
"application/vnd.github.v3+json".to_string(),
);
headers.insert(
CaseInsensitiveString::new("User-Agent"),
"subconverter-rs-example".to_string(),
);
let mut client_request = client.get(url);
for (key, value) in &headers {
client_request = client_request.insert_header((key.as_ref(), value.as_str()));
}
match client_request.send().await {
Ok(mut response) => {
let status = response.status();
if status.is_success() {
match response.json::<Value>().await {
Ok(gists) => {
if let Some(first_gist) = gists.as_array().and_then(|arr| arr.get(0)) {
if let (Some(id), Some(login)) = (
first_gist["id"].as_str(),
first_gist["owner"]["login"].as_str(),
) {
log::info!("Found existing Gist: ID={}, Owner={}", id, login);
Some((id.to_string(), login.to_string()))
} else {
log::warn!(
"First Gist found but missing 'id' or 'owner.login' fields."
);
None
}
} else {
log::warn!("No Gists found for the provided token.");
None
}
}
Err(e) => {
log::error!("Failed to parse Gist list response JSON: {}", e);
None
}
}
} else {
let body = response
.body()
.await
.map(|b| String::from_utf8_lossy(&b).to_string())
.unwrap_or_else(|_| "<failed to read body>".to_string());
log::error!(
"Failed to fetch Gist list: Status {}, Body: {}",
status,
body
);
None
}
}
Err(e) => {
log::error!("HTTP request to fetch Gist list failed: {}", e);
None
}
}
}
async fn setup_dummy_ini(token: Option<&str>, id: Option<&str>, username: Option<&str>) {
let mut content = "[common]\n".to_string();
if let Some(t) = token {
content.push_str(&format!("token={}\n", t));
}
if let Some(i) = id {
content.push_str(&format!("id={}\n", i));
}
if let Some(u) = username {
content.push_str(&format!("username={}\n", u));
}
let mut file = fs::File::create(DUMMY_INI_PATH).expect("Failed to create dummy ini");
file.write_all(content.as_bytes())
.expect("Failed to write dummy ini");
println!(
"Created dummy {} with content:\n{}",
DUMMY_INI_PATH, content
);
}
fn cleanup_dummy_ini() {
match fs::remove_file(DUMMY_INI_PATH) {
Ok(_) => println!("Cleaned up dummy {}", DUMMY_INI_PATH),
Err(e) => println!("Failed to clean up dummy {}: {}", DUMMY_INI_PATH, e),
}
}
#[actix_web::main]
async fn main() {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
println!("--- Running upload_gist example ---");
let github_token = env::var("GITHUB_TOKEN").ok();
let existing_gist_details = if let Some(token) = github_token.as_deref() {
fetch_first_gist_details(token).await
} else {
log::warn!("GITHUB_TOKEN environment variable not set. Update tests will use dummy data or may fail.");
None
};
println!("\n--- Test Case 1: No INI file ---");
let _ = fs::remove_file(DUMMY_INI_PATH);
match upload_gist(
"MyConfigName",
"my_subconverter_config.txt".to_string(),
"This is the content for the new gist file.".to_string(),
false, )
.await
{
Ok(_) => println!("upload_gist (Test 1) succeeded."),
Err(e) => println!("upload_gist (Test 1) failed: {}", e),
}
println!("\n--- Test Case 2: INI file without token ---");
let (test2_id, test2_user) = existing_gist_details
.as_ref()
.map(|(id, user)| (id.as_str(), user.as_str()))
.unwrap_or(("some_dummy_gist_id", "some_dummy_username"));
log::info!(
"Using Gist ID: {}, Username: {} for Test 2",
test2_id,
test2_user
);
setup_dummy_ini(None, None, Some(test2_user)).await;
match upload_gist(
"AnotherConfig",
"another_config.yaml".to_string(),
"YAML content here".to_string(),
true, )
.await
{
Ok(_) => println!("upload_gist (Test 2) succeeded."),
Err(e) => println!("upload_gist (Test 2) failed: {}", e),
}
cleanup_dummy_ini();
println!("\n--- Test Case 3: INI file with token ---");
setup_dummy_ini(github_token.as_deref(), None, None).await; match upload_gist(
"IniTokenTest",
"test_from_ini.txt".to_string(),
"Content using token from INI file.".to_string(),
false,
)
.await
{
Ok(_) => println!("upload_gist (Test 3) succeeded."),
Err(e) => println!("upload_gist (Test 3) failed: {}", e),
}
println!("\n--- Test Case 4: INI file with token, id, username (update) ---");
let (test4_id, test4_user) = existing_gist_details
.as_ref()
.map(|(id, user)| (id.as_str(), user.as_str()))
.unwrap_or(("another_dummy_gist_id", "another_dummy_username"));
log::info!(
"Using Gist ID: {}, Username: {} for Test 4",
test4_id,
test4_user
);
setup_dummy_ini(
github_token.as_deref(), None,
Some(test4_user),
)
.await;
match upload_gist(
"UpdateTest",
"update_test.txt".to_string(), "Updated content using token from INI file.".to_string(),
true,
)
.await
{
Ok(_) => println!("upload_gist (Test 4) succeeded."),
Err(e) => println!("upload_gist (Test 4) failed: {}", e),
}
println!("\n--- Cleaning up --- ");
cleanup_dummy_ini();
println!("--- Example finished ---");
}