1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct SessionConfig {
8 pub api_base: String,
9 pub session_token: String,
10 #[serde(default)]
12 pub last_character_id: Option<Uuid>,
13}
14
15impl SessionConfig {
16 pub fn path() -> anyhow::Result<PathBuf> {
17 if let Ok(p) = std::env::var("FLATLAND_SESSION_PATH") {
18 if !p.is_empty() {
19 return Ok(PathBuf::from(p));
20 }
21 }
22 let base = dirs::config_dir()
23 .ok_or_else(|| anyhow::anyhow!("could not resolve config directory"))?;
24 Ok(base.join("flatland").join("session.json"))
25 }
26
27 pub fn load() -> anyhow::Result<Self> {
28 let path = Self::path()?;
29 let bytes = std::fs::read(&path)
30 .map_err(|_| anyhow::anyhow!("not logged in; run `flatland3 auth login`"))?;
31 Ok(serde_json::from_slice(&bytes)?)
32 }
33
34 pub fn save(&self) -> anyhow::Result<()> {
35 let path = Self::path()?;
36 if let Some(parent) = path.parent() {
37 std::fs::create_dir_all(parent)?;
38 }
39 std::fs::write(path, serde_json::to_vec_pretty(self)?)?;
40 Ok(())
41 }
42
43 pub fn remember_character(&mut self, character_id: Uuid) -> anyhow::Result<()> {
44 if self.last_character_id == Some(character_id) {
45 return Ok(());
46 }
47 self.last_character_id = Some(character_id);
48 self.save()
49 }
50
51 #[allow(dead_code)] pub fn clear() -> anyhow::Result<()> {
54 let path = Self::path()?;
55 match std::fs::remove_file(&path) {
56 Ok(()) => Ok(()),
57 Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(()),
58 Err(err) => Err(err.into()),
59 }
60 }
61
62 #[allow(dead_code)] pub fn exists() -> bool {
64 Self::path().ok().map(|p| p.is_file()).unwrap_or(false)
65 }
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct SavedLogin {
75 pub email: String,
76 pub password: String,
77 #[serde(default, skip_serializing_if = "Option::is_none")]
78 pub api_base: Option<String>,
79}
80
81impl SavedLogin {
82 pub fn path() -> anyhow::Result<PathBuf> {
83 let base = dirs::config_dir()
84 .ok_or_else(|| anyhow::anyhow!("could not resolve config directory"))?;
85 Ok(base.join("flatland").join("login.json"))
86 }
87
88 #[allow(dead_code)] pub fn load() -> Option<Self> {
90 let path = Self::path().ok()?;
91 let bytes = std::fs::read(path).ok()?;
92 serde_json::from_slice(&bytes).ok()
93 }
94
95 #[allow(dead_code)] pub fn save(&self) -> anyhow::Result<()> {
97 let path = Self::path()?;
98 if let Some(parent) = path.parent() {
99 std::fs::create_dir_all(parent)?;
100 }
101 std::fs::write(&path, serde_json::to_vec_pretty(self)?)?;
102 #[cfg(unix)]
103 {
104 use std::os::unix::fs::PermissionsExt;
105 let _ = std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o600));
106 }
107 Ok(())
108 }
109
110 #[allow(dead_code)]
111 pub fn clear() -> anyhow::Result<()> {
112 let path = Self::path()?;
113 match std::fs::remove_file(&path) {
114 Ok(()) => Ok(()),
115 Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(()),
116 Err(err) => Err(err.into()),
117 }
118 }
119}
120
121pub fn default_api_base() -> String {
122 flatland_client_lib::default_api_base_url()
123}