dirigera 0.2.0

Manage your IKEA Trådfri devices with Rust
Documentation
#[cfg(feature = "binary")]
use std::collections::HashMap;
use std::io::Write;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let args: Vec<String> = std::env::args().collect();
    let ip_address = if args.len() < 2 {
        print!("Enter ip address: ");
        std::io::stdout().flush()?;

        let mut input = String::new();
        std::io::stdin().read_line(&mut input)?;

        input.trim().to_string()
    } else {
        args[1].to_string()
    };

    let file_path = "config.toml";
    if std::path::Path::new(file_path).exists() {
        anyhow::bail!("'config.toml' already exist!");
    }

    let client = reqwest::Client::builder()
        .danger_accept_invalid_certs(true)
        .build()
        .unwrap();

    let code_verify = pkce::code_verifier(128);
    let code_challenge = pkce::code_challenge(&code_verify);
    let code_veirifier_string = String::from_utf8(code_verify)?;

    let auth_url = reqwest::Url::parse_with_params(
        format!("https://{}:8443/v1/oauth/authorize", ip_address).as_str(),
        &[
            ("audience", "homesmart.local"),
            ("response_type", "code"),
            ("code_challenge", code_challenge.as_str()),
            ("code_challenge_method", "S256"),
        ],
    )?;

    let response: HashMap<String, String> = client.get(auth_url).send().await?.json().await?;
    let code = response
        .get("code")
        .ok_or_else(|| anyhow::anyhow!("code not found in body: {:?}", response))?;

    println!("Press ENTER after pressing the button on your Dirigera device");

    let mut input = String::new();
    std::io::stdin().read_line(&mut input)?;

    let mut token_params = HashMap::new();
    token_params.insert("code", code.as_str());
    token_params.insert("name", "localhost");
    token_params.insert("grant_type", "authorization_code");
    token_params.insert("code_verifier", code_veirifier_string.as_str());

    let token_url =
        reqwest::Url::parse(format!("https://{}:8443/v1/oauth/token", ip_address).as_str())?;

    let response: HashMap<String, String> = client
        .post(token_url)
        .json(&token_params)
        .send()
        .await?
        .json()
        .await?;

    let access_token = response
        .get("access_token")
        .ok_or_else(|| anyhow::anyhow!("code not found in body: {:?}", response))?;

    let mut config = toml::value::Table::new();
    config.insert(
        "ip-address".to_string(),
        toml::Value::String(ip_address.to_string()),
    );
    config.insert(
        "token".to_string(),
        toml::Value::String(access_token.to_string()),
    );

    let mut file = std::fs::File::create(file_path)?;
    let toml_string = toml::to_string(&config)?;
    file.write_all(toml_string.as_bytes())?;

    println!("🎉 Configuration has been saved to 'config.toml'");

    Ok(())
}