mirror of
https://github.com/asciinema/asciinema.git
synced 2025-12-16 11:48:13 +01:00
Unify config
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
use crate::{config::Config, util};
|
use crate::config::Config;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
@@ -8,8 +8,9 @@ pub struct Cli {}
|
|||||||
|
|
||||||
impl Cli {
|
impl Cli {
|
||||||
pub fn run(self, config: &Config) -> Result<()> {
|
pub fn run(self, config: &Config) -> Result<()> {
|
||||||
let auth_url = auth_url(config.server_url())?;
|
let server_url = config.get_server_url()?;
|
||||||
let server_hostname = auth_url.host().ok_or(anyhow!("invalid server URL"))?;
|
let server_hostname = server_url.host().ok_or(anyhow!("invalid server URL"))?;
|
||||||
|
let auth_url = auth_url(&server_url, &config.get_install_id()?);
|
||||||
|
|
||||||
println!("Open the following URL in a web browser to authenticate this asciinema CLI with your {server_hostname} user account:\n");
|
println!("Open the following URL in a web browser to authenticate this asciinema CLI with your {server_hostname} user account:\n");
|
||||||
println!("{}\n", auth_url);
|
println!("{}\n", auth_url);
|
||||||
@@ -19,9 +20,9 @@ impl Cli {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn auth_url(server_url: Option<&String>) -> Result<Url> {
|
fn auth_url(server_url: &Url, install_id: &str) -> Url {
|
||||||
let mut url = util::get_server_url(server_url)?;
|
let mut url = server_url.clone();
|
||||||
url.set_path(&format!("connect/{}", util::get_install_id()?));
|
url.set_path(&format!("connect/{install_id}"));
|
||||||
|
|
||||||
Ok(url)
|
url
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::util;
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use reqwest::{
|
use reqwest::{
|
||||||
@@ -27,9 +26,9 @@ impl Cli {
|
|||||||
let form = Form::new().file("asciicast", self.filename)?;
|
let form = Form::new().file("asciicast", self.filename)?;
|
||||||
|
|
||||||
let response = client
|
let response = client
|
||||||
.post(api_url(config.server_url())?)
|
.post(api_url(&config.get_server_url()?))
|
||||||
.multipart(form)
|
.multipart(form)
|
||||||
.basic_auth(get_username(), Some(util::get_install_id()?))
|
.basic_auth(get_username(), Some(config.get_install_id()?))
|
||||||
.header(header::USER_AGENT, build_user_agent())
|
.header(header::USER_AGENT, build_user_agent())
|
||||||
.header(header::ACCEPT, "application/json")
|
.header(header::ACCEPT, "application/json")
|
||||||
.send()?;
|
.send()?;
|
||||||
@@ -57,11 +56,11 @@ impl Cli {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn api_url(server_url: Option<&String>) -> Result<Url> {
|
fn api_url(server_url: &Url) -> Url {
|
||||||
let mut url = util::get_server_url(server_url)?;
|
let mut url = server_url.clone();
|
||||||
url.set_path("api/asciicasts");
|
url.set_path("api/asciicasts");
|
||||||
|
|
||||||
Ok(url)
|
url
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_username() -> String {
|
fn get_username() -> String {
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
|
use reqwest::Url;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::io::ErrorKind;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
const DEFAULT_SERVER_URL: &str = "https://asciinema.org";
|
||||||
|
const INSTALL_ID_FILENAME: &str = "install-id";
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
@@ -80,12 +86,46 @@ impl Config {
|
|||||||
Ok(config.build()?.try_deserialize()?)
|
Ok(config.build()?.try_deserialize()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn server_url(&self) -> Option<&String> {
|
pub fn get_server_url(&self) -> Result<Url> {
|
||||||
self.server.url.as_ref().or(self.api.url.as_ref())
|
match self.server.url.as_ref() {
|
||||||
|
Some(url) => Ok(Url::parse(url)?),
|
||||||
|
|
||||||
|
None => {
|
||||||
|
let url = Url::parse(&ask_for_server_url()?)?;
|
||||||
|
save_default_server_url(url.as_ref())?;
|
||||||
|
|
||||||
|
Ok(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_install_id(&self) -> Result<String> {
|
||||||
|
let path = install_id_path()?;
|
||||||
|
|
||||||
|
if let Some(id) = read_install_id(&path)? {
|
||||||
|
Ok(id)
|
||||||
|
} else {
|
||||||
|
let id = create_install_id();
|
||||||
|
save_install_id(&path, &id)?;
|
||||||
|
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save_default_server_url(url: &str) -> Result<()> {
|
fn ask_for_server_url() -> Result<String> {
|
||||||
|
println!("No asciinema server configured for this CLI.");
|
||||||
|
let mut rl = rustyline::DefaultEditor::new()?;
|
||||||
|
let url = rl.readline_with_initial(
|
||||||
|
"Enter the server URL to use by default: ",
|
||||||
|
(DEFAULT_SERVER_URL, ""),
|
||||||
|
)?;
|
||||||
|
println!();
|
||||||
|
|
||||||
|
Ok(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_default_server_url(url: &str) -> Result<()> {
|
||||||
let path = user_defaults_path()?;
|
let path = user_defaults_path()?;
|
||||||
|
|
||||||
if let Some(dir) = path.parent() {
|
if let Some(dir) = path.parent() {
|
||||||
@@ -97,12 +137,32 @@ pub fn save_default_server_url(url: &str) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn home() -> Result<PathBuf> {
|
fn read_install_id(path: &PathBuf) -> Result<Option<String>> {
|
||||||
env::var("ASCIINEMA_CONFIG_HOME")
|
match fs::read_to_string(path) {
|
||||||
.map(PathBuf::from)
|
Ok(s) => Ok(Some(s.trim().to_string())),
|
||||||
.or(env::var("XDG_CONFIG_HOME").map(|home| Path::new(&home).join("asciinema")))
|
|
||||||
.or(env::var("HOME").map(|home| Path::new(&home).join(".config").join("asciinema")))
|
Err(e) => {
|
||||||
.map_err(|_| anyhow!("need $HOME or $XDG_CONFIG_HOME or $ASCIINEMA_CONFIG_HOME"))
|
if e.kind() == ErrorKind::NotFound {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
bail!(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_install_id() -> String {
|
||||||
|
Uuid::new_v4().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_install_id(path: &PathBuf, id: &str) -> Result<()> {
|
||||||
|
if let Some(dir) = path.parent() {
|
||||||
|
fs::create_dir_all(dir)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::write(path, &id)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn user_config_path() -> Result<PathBuf> {
|
fn user_config_path() -> Result<PathBuf> {
|
||||||
@@ -112,3 +172,15 @@ fn user_config_path() -> Result<PathBuf> {
|
|||||||
fn user_defaults_path() -> Result<PathBuf> {
|
fn user_defaults_path() -> Result<PathBuf> {
|
||||||
Ok(home()?.join("defaults.toml"))
|
Ok(home()?.join("defaults.toml"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn install_id_path() -> Result<PathBuf> {
|
||||||
|
Ok(home()?.join(INSTALL_ID_FILENAME))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn home() -> Result<PathBuf> {
|
||||||
|
env::var("ASCIINEMA_CONFIG_HOME")
|
||||||
|
.map(PathBuf::from)
|
||||||
|
.or(env::var("XDG_CONFIG_HOME").map(|home| Path::new(&home).join("asciinema")))
|
||||||
|
.or(env::var("HOME").map(|home| Path::new(&home).join(".config").join("asciinema")))
|
||||||
|
.map_err(|_| anyhow!("need $HOME or $XDG_CONFIG_HOME or $ASCIINEMA_CONFIG_HOME"))
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ mod format;
|
|||||||
mod locale;
|
mod locale;
|
||||||
mod pty;
|
mod pty;
|
||||||
mod recorder;
|
mod recorder;
|
||||||
mod util;
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
|||||||
111
src/util.rs
111
src/util.rs
@@ -1,111 +0,0 @@
|
|||||||
use crate::config;
|
|
||||||
use anyhow::{anyhow, bail, Result};
|
|
||||||
use reqwest::Url;
|
|
||||||
use std::{env, fs, io::ErrorKind, path::Path, path::PathBuf};
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
const DEFAULT_SERVER_URL: &str = "https://asciinema.org";
|
|
||||||
const INSTALL_ID_FILENAME: &str = "install-id";
|
|
||||||
|
|
||||||
pub fn get_install_id() -> Result<String> {
|
|
||||||
if let Some(install_id) = read_install_id()? {
|
|
||||||
Ok(install_id)
|
|
||||||
} else if let Some(install_id) = read_legacy_install_id()? {
|
|
||||||
Ok(install_id)
|
|
||||||
} else {
|
|
||||||
create_install_id()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_install_id() -> Result<Option<String>> {
|
|
||||||
read_state_file(INSTALL_ID_FILENAME)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_install_id() -> Result<String> {
|
|
||||||
let id = Uuid::new_v4().to_string();
|
|
||||||
write_state_file(INSTALL_ID_FILENAME, &id)?;
|
|
||||||
|
|
||||||
Ok(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_legacy_install_id() -> Result<Option<String>> {
|
|
||||||
let path = config::home()?.join(INSTALL_ID_FILENAME);
|
|
||||||
|
|
||||||
match fs::read_to_string(path) {
|
|
||||||
Ok(s) => Ok(Some(s.trim().to_string())),
|
|
||||||
|
|
||||||
Err(e) => {
|
|
||||||
if e.kind() == ErrorKind::NotFound {
|
|
||||||
Ok(None)
|
|
||||||
} else {
|
|
||||||
bail!(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_server_url(server_url: Option<&String>) -> Result<Url> {
|
|
||||||
match server_url {
|
|
||||||
Some(url) => Ok(Url::parse(&url)?),
|
|
||||||
|
|
||||||
None => {
|
|
||||||
let url = Url::parse(&ask_for_server_url()?)?;
|
|
||||||
config::save_default_server_url(url.as_ref())?;
|
|
||||||
|
|
||||||
Ok(url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ask_for_server_url() -> Result<String> {
|
|
||||||
println!("No asciinema server configured for this CLI.");
|
|
||||||
let mut rl = rustyline::DefaultEditor::new()?;
|
|
||||||
let url = rl.readline_with_initial(
|
|
||||||
"Enter the server URL to use by default: ",
|
|
||||||
(DEFAULT_SERVER_URL, ""),
|
|
||||||
)?;
|
|
||||||
println!();
|
|
||||||
|
|
||||||
Ok(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_state_file(filename: &str) -> Result<Option<String>> {
|
|
||||||
let path = state_home()?.join(filename);
|
|
||||||
|
|
||||||
match fs::read_to_string(path) {
|
|
||||||
Ok(s) => Ok(Some(s.trim().to_string())),
|
|
||||||
|
|
||||||
Err(e) => {
|
|
||||||
if e.kind() == ErrorKind::NotFound {
|
|
||||||
Ok(None)
|
|
||||||
} else {
|
|
||||||
bail!(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_state_file(filename: &str, contents: &str) -> Result<()> {
|
|
||||||
let path = state_home()?.join(filename);
|
|
||||||
|
|
||||||
if let Some(dir) = path.parent() {
|
|
||||||
fs::create_dir_all(dir)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
fs::write(path, contents)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn state_home() -> Result<PathBuf> {
|
|
||||||
env::var("ASCIINEMA_STATE_HOME")
|
|
||||||
.map(PathBuf::from)
|
|
||||||
.or(env::var("XDG_STATE_HOME").map(|home| Path::new(&home).join("asciinema")))
|
|
||||||
.or(env::var("HOME").map(|home| {
|
|
||||||
Path::new(&home)
|
|
||||||
.join(".local")
|
|
||||||
.join("state")
|
|
||||||
.join("asciinema")
|
|
||||||
}))
|
|
||||||
.map_err(|_| anyhow!("need $HOME or $XDG_STATE_HOME or $ASCIINEMA_STATE_HOME"))
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user