use shep_core::paths::ShepPaths;
use shep_core::permissions::Grants;
use crate::commands::shep_toml::{GrantRecord, ShepToml};
use crate::exit::ExitCode;
use crate::output::Streams;
use super::consent::{Input, not_confined};
use super::fail_config;
use super::upgrade::upgrade_one;
use crate::commands::copy::confirm::{Answer, read_answer};
const UNREVIEWED_NOTICE: &str = "dog_grants_unreviewed";
const GRANTS_NOTICE: &str = "dog_grants";
pub(super) fn second_look(
streams: &mut Streams<'_>,
paths: &ShepPaths,
name: &str,
yes: bool,
input: &mut Input,
) -> Result<(), ExitCode> {
upgrade_one(paths, name).map_err(|err| fail_config(streams, &err))?;
let record = ShepToml::read_only(&paths.daemon_config)
.and_then(|cfg| cfg.grant_record(name))
.map_err(|err| fail_config(streams, &err))?;
let Some(record) = record else {
return Ok(());
};
if record.waiting.names().next().is_none() && record.reviewed {
return Ok(());
}
let listing = listing(name, &record);
if !yes && !input.is_terminal {
streams.aside(
UNREVIEWED_NOTICE,
&format!(
"{listing}\nleft as they are: there is no terminal to ask on. Run `shep enable \
{name} --yes` to approve them"
),
);
return Ok(());
}
streams.aside(GRANTS_NOTICE, &format!("{listing}\n{}", not_confined(name)));
if !yes {
let question = if record.waiting.names().next().is_some() {
format!("grant {name} the new permissions too? [y/N] ")
} else {
format!("keep these grants for {name}? [y/N] ")
};
streams.aside("confirm", &question);
match read_answer(&mut input.reader) {
Answer::Yes => {}
Answer::No => return Ok(()),
Answer::Failed(reason) => {
return Err(streams.fail(
ExitCode::Failure,
&format!(
"no permission was approved: the question could not be read ({reason})"
),
));
}
}
}
let approved = GrantRecord {
granted: Grants::from_names(
record
.granted
.names()
.chain(record.waiting.names())
.map(str::to_owned),
),
waiting: Grants::default(),
reviewed: true,
..record.clone()
};
let written = ShepToml::try_edit(&paths.daemon_config, |cfg| {
if cfg.grant_record(name)?.as_ref() != Some(&record) {
return Ok(false);
}
cfg.set_grant_record(name, &approved).map(|()| true)
})
.map_err(|err| fail_config(streams, &err))?;
if written {
return Ok(());
}
Err(streams.fail(
ExitCode::Failure,
&format!(
"no permission was approved: the grants for {name} changed while the question was \
open. Run `shep enable {name}` again to see them"
),
))
}
fn listing(name: &str, record: &GrantRecord) -> String {
let names = |grants: &Grants| grants.names().collect::<Vec<_>>().join(", ");
let mut lines = Vec::new();
if record.granted.names().next().is_some() {
let mark = if record.reviewed {
""
} else {
" (granted at upgrade, not reviewed)"
};
lines.push(format!("{name} holds: {}{mark}", names(&record.granted)));
} else if !record.reviewed {
lines.push(format!(
"{name} holds no grants (granted at upgrade, not reviewed)"
));
}
if record.waiting.names().next().is_some() {
lines.push(format!(
"{name}'s build now also asks for, and waits on: {}",
names(&record.waiting)
));
}
lines.join("\n")
}