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