use clap::Subcommand;
use objectiveai_cli_sdk::output::{Handle, Items, ListItem, Output, PairListItem};
#[derive(Subcommand)]
pub enum Source {
Filesystem,
Favorites,
Objectiveai,
Mock,
All,
}
async fn emit_items(items: Vec<ListItem>, handle: &Handle) {
Output::<Items<ListItem>>::Notification(objectiveai_cli_sdk::output::Notification { value: Items { items } })
.emit(handle)
.await;
}
async fn emit_pair_items(items: Vec<PairListItem>, handle: &Handle) {
Output::<Items<PairListItem>>::Notification(objectiveai_cli_sdk::output::Notification { value: Items { items } })
.emit(handle)
.await;
}
fn favorite_matches(fav: &objectiveai_sdk::filesystem::config::Favorite, path: &objectiveai_sdk::RemotePath) -> bool {
favorite_matches_path(fav.path(), path)
}
pub async fn favorites<F, Fut>(
get_favorites: F,
handle: &Handle,
) -> Result<(), crate::error::Error>
where
F: FnOnce() -> Fut,
Fut: std::future::Future<Output = Vec<objectiveai_sdk::filesystem::config::Favorite>>,
{
let items: Vec<ListItem> = get_favorites().await
.into_iter()
.map(ListItem::Favorite)
.collect();
emit_items(items, handle).await;
Ok(())
}
pub async fn single<F>(
list_remote: F,
handle: &Handle,
) -> Result<(), crate::error::Error>
where
F: FnOnce(objectiveai_sdk::HttpClient) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Vec<objectiveai_sdk::RemotePath>, crate::error::Error>> + Send>> + Send + 'static,
{
let handle = handle.clone();
crate::api::run(move |http_client| Box::pin(async move {
let items: Vec<ListItem> = list_remote(http_client).await?
.into_iter()
.map(ListItem::Item)
.collect();
emit_items(items, &handle).await;
Ok(())
}), false).await
}
pub async fn all<F, Fut, FsF, OaiF>(
get_favorites: F,
list_filesystem: FsF,
list_objectiveai: OaiF,
handle: &Handle,
) -> Result<(), crate::error::Error>
where
F: FnOnce() -> Fut,
Fut: std::future::Future<Output = Vec<objectiveai_sdk::filesystem::config::Favorite>>,
FsF: FnOnce(objectiveai_sdk::HttpClient) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Vec<objectiveai_sdk::RemotePath>, crate::error::Error>> + Send>> + Send + 'static,
OaiF: FnOnce(objectiveai_sdk::HttpClient) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Vec<objectiveai_sdk::RemotePath>, crate::error::Error>> + Send>> + Send + 'static,
{
let favorites = get_favorites().await;
let handle = handle.clone();
crate::api::run(move |http_client| Box::pin(async move {
let (fs_result, oai_result) = tokio::join!(
list_filesystem(http_client.clone()),
list_objectiveai(http_client),
);
let fs_items = fs_result?;
let oai_items = oai_result?;
let mut items: Vec<ListItem> = Vec::new();
for fav in &favorites {
items.push(ListItem::Favorite(fav.clone()));
}
for item in fs_items {
if !favorites.iter().any(|fav| favorite_matches(fav, &item)) {
items.push(ListItem::Item(item));
}
}
for item in oai_items {
let dominated = favorites.iter().any(|fav| favorite_matches(fav, &item))
|| items.iter().any(|existing| match existing {
ListItem::Item(p) => p == &item,
_ => false,
});
if !dominated {
items.push(ListItem::Item(item));
}
}
emit_items(items, &handle).await;
Ok(())
}), false).await
}
fn favorite_matches_path(
fav_path: &objectiveai_sdk::RemotePathCommitOptional,
path: &objectiveai_sdk::RemotePath,
) -> bool {
match (fav_path, path) {
(
objectiveai_sdk::RemotePathCommitOptional::Github { owner: fo, repository: fr, commit: fc },
objectiveai_sdk::RemotePath::Github { owner: po, repository: pr, commit: pc },
) => fo == po && fr == pr && fc.as_ref().is_none_or(|c| c == pc),
(
objectiveai_sdk::RemotePathCommitOptional::Filesystem { owner: fo, repository: fr, commit: fc },
objectiveai_sdk::RemotePath::Filesystem { owner: po, repository: pr, commit: pc },
) => fo == po && fr == pr && fc.as_ref().is_none_or(|c| c == pc),
(
objectiveai_sdk::RemotePathCommitOptional::Mock { name: fn_ },
objectiveai_sdk::RemotePath::Mock { name: pn },
) => fn_ == pn,
_ => false,
}
}
fn pair_favorite_matches(
fav: &objectiveai_sdk::filesystem::config::PairFavorite,
item: &objectiveai_sdk::functions::response::ListFunctionProfilePairItem,
) -> bool {
favorite_matches_path(&fav.function, &item.function)
&& favorite_matches_path(&fav.profile, &item.profile)
}
pub async fn pair_favorites<F, Fut>(
get_favorites: F,
handle: &Handle,
) -> Result<(), crate::error::Error>
where
F: FnOnce() -> Fut,
Fut: std::future::Future<Output = Vec<objectiveai_sdk::filesystem::config::PairFavorite>>,
{
let items: Vec<PairListItem> = get_favorites().await
.into_iter()
.map(PairListItem::Favorite)
.collect();
emit_pair_items(items, handle).await;
Ok(())
}
pub async fn pair_single<F>(
list_remote: F,
handle: &Handle,
) -> Result<(), crate::error::Error>
where
F: FnOnce(objectiveai_sdk::HttpClient) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Vec<objectiveai_sdk::functions::response::ListFunctionProfilePairItem>, crate::error::Error>> + Send>> + Send + 'static,
{
let handle = handle.clone();
crate::api::run(move |http_client| Box::pin(async move {
let items: Vec<PairListItem> = list_remote(http_client).await?
.into_iter()
.map(PairListItem::Item)
.collect();
emit_pair_items(items, &handle).await;
Ok(())
}), false).await
}
pub async fn pair_all<GF, GFut, F>(
get_favorites: GF,
list_objectiveai: F,
handle: &Handle,
) -> Result<(), crate::error::Error>
where
GF: FnOnce() -> GFut,
GFut: std::future::Future<Output = Vec<objectiveai_sdk::filesystem::config::PairFavorite>>,
F: FnOnce(objectiveai_sdk::HttpClient) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Vec<objectiveai_sdk::functions::response::ListFunctionProfilePairItem>, crate::error::Error>> + Send>> + Send + 'static,
{
let favorites = get_favorites().await;
let handle = handle.clone();
crate::api::run(move |http_client| Box::pin(async move {
let oai_items = list_objectiveai(http_client).await?;
let mut items: Vec<PairListItem> = Vec::new();
for fav in &favorites {
items.push(PairListItem::Favorite(fav.clone()));
}
for item in oai_items {
if !favorites.iter().any(|fav| pair_favorite_matches(fav, &item)) {
items.push(PairListItem::Item(item));
}
}
emit_pair_items(items, &handle).await;
Ok(())
}), false).await
}