1use super::GrantMode;
14use super::lys_pass_adapter::{LiveAnswer, LiveLysAsk};
15use lys_pass::deliberate::CheckAnswer;
16use lys_pass::drafts::{DraftRequest, request_digest};
17use lys_pass::{Client, Error, Mode, Target};
18
19pub struct HttpLysAsk {
21 client: Client,
22 handle: tokio::runtime::Handle,
23}
24
25impl std::fmt::Debug for HttpLysAsk {
26 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 formatter.debug_struct("HttpLysAsk").finish_non_exhaustive()
28 }
29}
30
31impl HttpLysAsk {
32 pub fn new(issuer: &str, handle: tokio::runtime::Handle) -> Result<Self, Error> {
37 let issuer = issuer
38 .parse()
39 .map_err(|_| Error::Invalid("issuer is not a URL"))?;
40 Ok(Self {
41 client: Client::new(reqwest::Client::default(), issuer)?,
42 handle,
43 })
44 }
45
46 fn block<T, F>(&self, future: F) -> Result<T, Error>
48 where
49 T: Send,
50 F: std::future::Future<Output = Result<T, Error>> + Send,
51 {
52 let handle = &self.handle;
53 std::thread::scope(|scope| scope.spawn(move || handle.block_on(future)).join())
54 .unwrap_or_else(|_| Err(Error::CannotAsk("the live ask did not complete")))
55 }
56
57 fn draft(
60 &self,
61 pass: &str,
62 holder: &str,
63 target: &Target,
64 grant: &str,
65 judgment: &CheckAnswer,
66 ) -> Result<String, Error> {
67 let words = format!(
68 "{holder} asks {} on {}:{}",
69 target.action, target.kind, target.id
70 );
71 let draft = DraftRequest {
72 operation: format!(
73 "liminal:{holder}:{}:{}:{}",
74 target.kind, target.id, target.action
75 ),
76 grant: grant.to_owned(),
77 target: Target::new(&target.kind, &target.id, &target.action)?,
78 request_digest: request_digest(&words),
79 words,
80 };
81 self.block(self.client.create_draft(pass, &draft, judgment))
82 .map(|created| created.draft)
83 }
84}
85
86impl LiveLysAsk for HttpLysAsk {
87 fn ask(
88 &self,
89 pass: &str,
90 holder: &str,
91 target: &Target,
92 _now: u64,
93 ) -> Result<LiveAnswer, Error> {
94 let answer =
95 self.block(
96 self.client
97 .check_batch(pass, holder, std::slice::from_ref(target), None),
98 )?;
99 let Some(result) = answer.results.into_iter().next() else {
100 return Err(Error::CannotAsk("the live answer carried no result"));
101 };
102 match (
103 result.allowed,
104 result.mode.as_ref(),
105 result.grant.as_deref(),
106 result.refusal.as_deref(),
107 ) {
108 (true, Some(Mode::Outright), Some(grant), None) => Ok(LiveAnswer::Outright {
109 grant: grant.to_owned(),
110 }),
111 (false, Some(Mode::ByDraft), Some(grant), None) => Ok(LiveAnswer::Held {
112 mode: GrantMode::ByDraft,
113 grant: grant.to_owned(),
114 draft: self.draft(pass, holder, target, grant, &result)?,
115 }),
116 (false, Some(Mode::ByTwo), Some(grant), None) => Ok(LiveAnswer::Held {
117 mode: GrantMode::ByTwo,
118 grant: grant.to_owned(),
119 draft: self.draft(pass, holder, target, grant, &result)?,
120 }),
121 (false, None, None, Some(refusal)) => Ok(LiveAnswer::Refused {
122 refusal: refusal.to_owned(),
123 }),
124 _ => Err(Error::CannotAsk("inconsistent live permission answer")),
125 }
126 }
127}