use std::path::PathBuf;
use async_trait::async_trait;
use clap::{Arg, Command};
use std::str::FromStr;
use liboxen::api;
use liboxen::config::UserConfig;
use liboxen::constants::{DEFAULT_HOST, DEFAULT_SCHEME};
use liboxen::error::OxenError;
use liboxen::model::RepoNew;
use liboxen::model::file::{FileContents, FileNew};
use liboxen::storage::StorageKind;
use crate::cmd::RunCmd;
pub const NAME: &str = "create-remote";
pub struct CreateRemoteCmd;
#[async_trait]
impl RunCmd for CreateRemoteCmd {
fn name(&self) -> &str {
NAME
}
fn args(&self) -> Command {
Command::new(NAME)
.about("Creates a remote repository with the name on the host. Default behavior is to create a remote on the hub.oxen.ai remote.")
.arg(
Arg::new("name")
.long("name")
.short('n')
.help("The namespace/name of the remote repository you want to create. For example: 'ox/my_repo'")
.required(true)
.action(clap::ArgAction::Set),
)
.arg(
Arg::new("host")
.long("host")
.help("The host you want to create the remote repository on. For example: 'hub.oxen.ai'")
.action(clap::ArgAction::Set),
)
.arg(
Arg::new("scheme")
.long("scheme")
.help("The scheme for the url of the remote repository. For example: 'https' or 'http'")
.action(clap::ArgAction::Set),
)
.arg(
Arg::new("add_readme")
.long("add_readme")
.help("If present, it will create a README file and initial commit in the remote repo.")
.action(clap::ArgAction::SetTrue),
)
.arg(
Arg::new("is_public")
.long("is_public")
.short('p')
.help("If present, it will create a public remote repository.")
.action(clap::ArgAction::SetTrue),
)
.arg(
Arg::new("storage-backend")
.long("storage-backend")
.help("Pick which storage backend the server should use for this repo. Must match a backend the server has configured; otherwise the server will reject the request.")
.value_parser(["local", "s3"])
.action(clap::ArgAction::Set),
)
}
async fn run(&self, args: &clap::ArgMatches) -> Result<(), OxenError> {
let Some(namespace_name) = args.get_one::<String>("name") else {
return Err(OxenError::basic_str(
"Must supply a namespace/name for the remote repository.",
));
};
let storage_kind = args
.get_one::<String>("storage-backend")
.map(|s| StorageKind::from_str(s))
.transpose()?;
let host = args
.get_one::<String>("host")
.map(String::from)
.unwrap_or(DEFAULT_HOST.to_string());
let scheme = args
.get_one::<String>("scheme")
.map(String::from)
.unwrap_or(DEFAULT_SCHEME.to_string());
let parts: Vec<&str> = namespace_name.split('/').collect();
if parts.len() != 2 {
return Err(OxenError::basic_str(
"Invalid name format. Must be namespace/name",
));
}
let namespace = parts[0];
let name = parts[1];
let empty = !args.get_flag("add_readme");
let is_public = args.get_flag("is_public");
if empty {
let mut repo_new = RepoNew::from_namespace_name(namespace, name, storage_kind);
repo_new.host = Some(host);
repo_new.is_public = Some(is_public);
repo_new.scheme = Some(scheme);
let remote_repo = api::client::repositories::create_empty(repo_new).await?;
println!(
"🎉 Remote successfully created for '{}/{}'\n\nIf this is a brand new repository:\n\n oxen clone {}\n\nTo push an existing local repository to a new remote:\n\n oxen config --set-remote origin {}\n",
namespace, name, remote_repo.remote.url, remote_repo.remote.url
);
} else {
let config = UserConfig::get()?;
let user = config.to_user();
let readme_body = format!(
"
Welcome to Oxen.ai 🐂 🌾
## Getting Started
Clone the repository to your local machine:
```bash
oxen clone https://{host}/{namespace}/{name}
```
## Adding Data
You can add files to it with
```
oxen add <path>
```
Then commit them with
```
oxen commit -m <message>
```
## Pushing Data
Push your changes to the remote with
```
oxen push origin main
```
## Learn More
For the complete developer documentation, visit https://docs.oxen.ai/
Happy Mooooooving of data 🐂
"
);
let files: Vec<FileNew> = vec![FileNew {
path: PathBuf::from("README.md"),
contents: FileContents::Text(format!("# {name}\n{readme_body}")),
user,
}];
let mut repo = RepoNew::from_files(namespace, name, files, storage_kind);
repo.host = Some(host);
repo.is_public = Some(is_public);
repo.scheme = Some(scheme);
let remote_repo = api::client::repositories::create(repo).await?;
println!(
"🎉 Remote successfully created for '{}/{}'\n\nClone your repository with:\n\n oxen clone {}\n",
namespace, name, remote_repo.remote.url
);
}
Ok(())
}
}