use crate::settings::Settings;
use crate::utils::file::file_exists;
use crate::utils::http::{parse_proxy, HttpResponse, ProxyConfig};
use crate::utils::http::{web_patch_async, web_post_async};
use crate::utils::ini_reader::{IniReader, IniReaderError};
use case_insensitive_string::CaseInsensitiveString;
use serde_json::json;
use std::collections::HashMap;
use std::env;
fn build_gist_data(name: &str, content: &str) -> String {
json!({
"description": "subconverter",
"public": false,
"files": {
name: {
"content": content
}
}
})
.to_string()
}
pub async fn upload_gist(
name: &str,
mut path: String,
mut content: String,
write_manage_url: bool,
) -> Result<String, String> {
let ini_path = "gistconf.ini";
if !file_exists(ini_path).await {
log::error!("gistconf.ini not found. Skipping...");
return Err("gistconf.ini not found".to_string());
}
let mut ini = IniReader::new();
if let Err(e) = ini.parse_file(ini_path).await {
let err_msg = format!("Failed to parse gistconf.ini: {}", e);
log::error!("{}", err_msg);
return Err(err_msg);
}
if ini.enter_section("common").is_err() {
log::error!("gistconf.ini has incorrect format ([common] section missing). Skipping...");
return Err("gistconf.ini has incorrect format".to_string());
}
let mut token = ini.get_current("token");
if token.is_empty() {
log::info!(
"Token not found in gistconf.ini, checking GITHUB_TOKEN environment variable..."
);
match env::var("GITHUB_TOKEN") {
Ok(env_token) if !env_token.is_empty() => {
token = env_token;
}
_ => {
log::error!(
"No token provided in gistconf.ini or GITHUB_TOKEN environment variable. Skipping..."
);
return Err("No token provided".to_string());
}
}
}
let mut id = ini.get_current("id");
let mut username = ini.get_current("username");
if path.is_empty() {
let path_from_common = ini.get_current(name);
if !path_from_common.is_empty() {
path = path_from_common;
} else {
path = name.to_string();
}
}
let proxy_config = parse_proxy(&Settings::current().proxy_config);
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".to_string(),
);
let mut final_url = String::new(); let response: HttpResponse;
if id.is_empty() {
log::info!("No Gist id is provided. Creating new Gist...");
let gist_data = build_gist_data(&path, &content);
let api_url = "https://api.github.com/gists";
match web_post_async(api_url, gist_data, &proxy_config, Some(&headers)).await {
Ok(resp) => {
response = resp;
if response.status != 201 {
let err_msg = format!(
"Create new Gist failed!\nReturn code: {}\nReturn data:\n{}",
response.status, response.body
);
log::error!("{}", err_msg);
return Err("Failed to create Gist".to_string());
}
}
Err(e) => {
let err_msg = format!("Create new Gist HTTP request failed: {}", e);
log::error!("{}", err_msg);
return Err(err_msg);
}
}
} else {
log::info!("Gist id provided. Modifying Gist...");
let base_url = format!(
"https://gist.githubusercontent.com/{}/{}/raw/{}",
username, id, path
);
if write_manage_url {
content = format!("#!MANAGED-CONFIG {}\n{}", base_url, content);
}
let gist_data = build_gist_data(&path, &content);
let api_url = format!("https://api.github.com/gists/{}", id);
match web_patch_async(&api_url, gist_data, &proxy_config, Some(&headers)).await {
Ok(resp) => {
response = resp;
if response.status != 200 {
let err_msg = format!(
"Modify Gist failed!\nReturn code: {}\nReturn data:\n{}",
response.status, response.body
);
log::error!("{}", err_msg);
return Err("Failed to modify Gist".to_string());
}
}
Err(e) => {
let err_msg = format!("Modify Gist HTTP request failed: {}", e);
log::error!("{}", err_msg);
return Err(err_msg);
}
}
}
match serde_json::from_str::<serde_json::Value>(&response.body) {
Ok(json) => {
if let Some(new_id) = json["id"].as_str() {
id = new_id.to_string();
}
if let Some(owner_login) = json["owner"]["login"].as_str() {
username = owner_login.to_string();
}
final_url = format!(
"https://gist.githubusercontent.com/{}/{}/raw/{}",
username, id, path
);
let log_msg = format!(
"Writing to Gist success!\nGenerator: {}\nPath: {}\nRaw URL: {}\nGist owner: {}",
name, path, final_url, username
);
log::info!("{}", log_msg);
}
Err(e) => {
let err_msg = format!(
"Failed to parse Gist API response JSON: {}\nResponse body:\n{}",
e, response.body
);
log::error!("{}", err_msg);
}
}
if ini.enter_section("common").is_ok() {
ini.erase_section(); ini.set_current("token", &token).ok(); ini.set_current("id", &id).ok(); ini.set_current("username", &username).ok(); } else {
log::error!("Failed to re-enter [common] section to update ini.");
}
ini.set_current_section(&path);
ini.erase_section(); ini.set_current("type", name).ok();
ini.set_current("url", &final_url).ok();
if let Err(e) = ini.to_file(ini_path) {
let err_msg = format!("Failed to write updated gistconf.ini: {}", e);
log::error!("{}", err_msg);
return Err(err_msg);
}
Ok(final_url)
}