Skip to main content

lineark_sdk/
auth.rs

1use crate::error::LinearError;
2use std::path::PathBuf;
3
4/// Resolve a Linear API token from the filesystem.
5/// Reads `~/.linear_api_token` (linearis-compatible).
6pub fn token_from_file() -> Result<String, LinearError> {
7    let path = token_file_path()?;
8    std::fs::read_to_string(&path)
9        .map(|s| s.trim().to_string())
10        .map_err(|e| {
11            LinearError::AuthConfig(format!(
12                "Could not read token file {}: {}",
13                path.display(),
14                e
15            ))
16        })
17}
18
19/// Resolve a Linear API token from the environment variable `LINEAR_API_TOKEN`.
20pub fn token_from_env() -> Result<String, LinearError> {
21    std::env::var("LINEAR_API_TOKEN").map_err(|_| {
22        LinearError::AuthConfig("LINEAR_API_TOKEN environment variable not set".to_string())
23    })
24}
25
26/// Resolve a Linear API token with precedence: env var -> file.
27/// (CLI flag takes highest precedence but is handled at the CLI layer.)
28pub fn auto_token() -> Result<String, LinearError> {
29    token_from_env().or_else(|_| token_from_file())
30}
31
32fn token_file_path() -> Result<PathBuf, LinearError> {
33    let home = home::home_dir()
34        .ok_or_else(|| LinearError::AuthConfig("Could not determine home directory".to_string()))?;
35    Ok(home.join(".linear_api_token"))
36}
37
38#[cfg(test)]
39mod tests {
40    use super::*;
41
42    #[test]
43    fn token_from_env_success() {
44        // Temporarily set the env var. Use a unique test name to avoid races.
45        std::env::set_var("LINEAR_API_TOKEN", "test-token-12345");
46        let result = token_from_env();
47        std::env::remove_var("LINEAR_API_TOKEN");
48        assert_eq!(result.unwrap(), "test-token-12345");
49    }
50
51    #[test]
52    fn token_from_env_missing() {
53        std::env::remove_var("LINEAR_API_TOKEN");
54        let result = token_from_env();
55        assert!(result.is_err());
56        assert!(result.unwrap_err().to_string().contains("LINEAR_API_TOKEN"));
57    }
58
59    #[test]
60    fn auto_token_prefers_env() {
61        std::env::set_var("LINEAR_API_TOKEN", "env-token-auto");
62        let result = auto_token();
63        std::env::remove_var("LINEAR_API_TOKEN");
64        // Should use env var, not file
65        assert_eq!(result.unwrap(), "env-token-auto");
66    }
67
68    #[test]
69    fn token_file_path_is_home_based() {
70        let path = token_file_path().unwrap();
71        assert!(path.to_str().unwrap().contains(".linear_api_token"));
72        assert!(path.to_str().unwrap().starts_with("/"));
73    }
74}