1use keyring::Entry;
2use std::{
3 collections::HashMap,
4 fmt::Display,
5 io::{self, Read},
6};
7
8pub mod db;
9pub mod error;
10pub mod github;
11
12use error::GitError;
13
14#[allow(dead_code)] #[derive(Debug)]
18pub struct Request {
19 pub host: String,
20 protocol: String,
21 pub owner: String,
22}
23
24impl Request {
25 pub fn from_stdin() -> Result<Self, GitError> {
26 let mut buf = String::new();
27 let _ = io::stdin().read_to_string(&mut buf)?;
28 let stdin_info: HashMap<String, String> = buf
29 .lines()
30 .filter_map(|l| {
31 l.split_once("=")
32 .map(|(k, v)| (k.trim().to_string(), v.trim().to_string()))
33 })
34 .collect();
35
36 Ok(Request {
37 protocol: stdin_info
38 .get("protocol")
39 .ok_or(GitError::MissingInfo(String::from("protocol")))?
40 .to_string(),
41 host: stdin_info
42 .get("host")
43 .ok_or(GitError::MissingInfo(String::from("host")))?
44 .to_string(),
45 owner: stdin_info
46 .get("path")
47 .ok_or(GitError::MissingInfo(String::from("path")))?
48 .split_once("/")
49 .map(|s| s.0)
50 .ok_or(GitError::InvalidInfo(String::from(
51 "Provided path not complete",
52 )))?
53 .to_string(),
54 })
55 }
56}
57
58#[derive(Clone)]
59pub struct Login {
60 pub username: String,
61 pub host: String,
62 pub email: Option<String>,
63}
64
65impl Login {
66 pub fn new(username: String, host: String, email: Option<String>) -> Self {
67 Self {
68 username,
69 host,
70 email,
71 }
72 }
73
74 fn entry(&self) -> Entry {
75 let identifier = format!("{}@{}", self.username, self.host);
76 match Entry::new("git-auth", &identifier) {
77 Ok(entry) => entry,
78 Err(keyring::error::Error::TooLong(_, max)) => {
79 Entry::new("git-auth", identifier.split_at(max as usize).0)
80 .expect("Expected entry creation success after handling length")
81 }
82 Err(err) => panic!("Unrecoverable error in login creation:\n{err}"),
83 }
84 }
85
86 pub fn get_password(&self) -> keyring::Result<String> {
87 self.entry().get_password()
88 }
89 pub fn set_password(&self, password: &str) -> keyring::Result<()> {
90 self.entry().set_password(password)
91 }
92 pub fn delete_password(&self) -> keyring::Result<()> {
93 self.entry().delete_credential()
94 }
95}
96
97impl Display for Login {
98 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 match &self.email {
100 Some(email) => write!(f, "{}, ({})", self.username, email),
101 None => write!(f, "{}", self.username),
102 }
103 }
104}
105
106pub fn send_creds(creds: &Login) -> keyring::Result<()> {
107 println!("username={}", creds.username);
108 println!("password={}", creds.get_password()?);
109 println!("quit=1");
110 Ok(())
111}