objectiveai_sdk/cli/command/
path_ref.rs1use std::str::FromStr;
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
29pub enum Remote {
30 Github,
31 Filesystem,
32 Mock,
33}
34
35impl Remote {
36 pub fn into_path(
40 self,
41 owner: Option<String>,
42 repository: Option<String>,
43 name: Option<String>,
44 commit: Option<String>,
45 ) -> Option<crate::RemotePathCommitOptional> {
46 match self {
47 Remote::Github => Some(crate::RemotePathCommitOptional::Github {
48 owner: owner?,
49 repository: repository?,
50 commit,
51 }),
52 Remote::Filesystem => Some(crate::RemotePathCommitOptional::Filesystem {
53 owner: owner?,
54 repository: repository?,
55 commit,
56 }),
57 Remote::Mock => Some(crate::RemotePathCommitOptional::Mock { name: name? }),
58 }
59 }
60
61 fn parse_keyword(s: &str) -> Result<Self, String> {
62 match s {
63 "github" => Ok(Self::Github),
64 "filesystem" => Ok(Self::Filesystem),
65 "mock" => Ok(Self::Mock),
66 other => Err(format!(
67 "unknown remote: {other} (expected github, filesystem, or mock)"
68 )),
69 }
70 }
71}
72
73pub(crate) fn tokenize(s: &str) -> Result<Vec<(&str, &str)>, String> {
77 s.split(',')
78 .map(|pair| {
79 pair.split_once('=')
80 .map(|(k, v)| (k.trim(), v.trim()))
81 .ok_or_else(|| format!("expected key=value, got: {pair}"))
82 })
83 .collect()
84}
85
86impl FromStr for crate::RemotePathCommitOptional {
87 type Err = String;
88
89 fn from_str(s: &str) -> Result<Self, Self::Err> {
93 let mut remote: Option<Remote> = None;
94 let mut owner: Option<String> = None;
95 let mut repository: Option<String> = None;
96 let mut name: Option<String> = None;
97 let mut commit: Option<String> = None;
98 for (k, v) in tokenize(s)? {
99 match k {
100 "remote" => remote = Some(Remote::parse_keyword(v)?),
101 "owner" => owner = Some(v.to_string()),
102 "repository" => repository = Some(v.to_string()),
103 "name" => name = Some(v.to_string()),
104 "commit" => commit = Some(v.to_string()),
105 other => return Err(format!("unknown key: {other}")),
106 }
107 }
108 let remote = remote.ok_or_else(|| "remote is required".to_string())?;
109 remote
110 .into_path(owner, repository, name, commit)
111 .ok_or_else(|| {
112 "owner and repository are required for github/filesystem, name for mock"
113 .to_string()
114 })
115 }
116}
117
118#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
127#[serde(untagged)]
128#[schemars(rename = "cli.command.RemotePathCommitOptionalOrFavorite")]
129pub enum RemotePathCommitOptionalOrFavorite {
130 #[schemars(title = "Resolved")]
131 Resolved(crate::RemotePathCommitOptional),
132 #[schemars(title = "Favorite")]
133 Favorite(String),
134}
135
136impl RemotePathCommitOptionalOrFavorite {
137 pub fn into_arg_string(&self) -> String {
141 match self {
142 Self::Favorite(name) => format!("favorite={name}"),
143 Self::Resolved(path) => remote_path_to_arg_string(path),
144 }
145 }
146}
147
148impl FromStr for RemotePathCommitOptionalOrFavorite {
149 type Err = String;
150
151 fn from_str(s: &str) -> Result<Self, Self::Err> {
157 let pairs = tokenize(s)?;
158 if let Some((_, value)) = pairs.iter().find(|(k, _)| *k == "favorite") {
159 if pairs.len() > 1 {
160 return Err(
161 "favorite= cannot be combined with other keys".to_string(),
162 );
163 }
164 return Ok(Self::Favorite((*value).to_string()));
165 }
166 s.parse::<crate::RemotePathCommitOptional>().map(Self::Resolved)
167 }
168}
169
170pub fn remote_path_to_arg_string(path: &crate::RemotePathCommitOptional) -> String {
173 match path {
174 crate::RemotePathCommitOptional::Github {
175 owner,
176 repository,
177 commit,
178 } => {
179 let mut s = format!("remote=github,owner={owner},repository={repository}");
180 if let Some(c) = commit {
181 s.push_str(&format!(",commit={c}"));
182 }
183 s
184 }
185 crate::RemotePathCommitOptional::Filesystem {
186 owner,
187 repository,
188 commit,
189 } => {
190 let mut s = format!("remote=filesystem,owner={owner},repository={repository}");
191 if let Some(c) = commit {
192 s.push_str(&format!(",commit={c}"));
193 }
194 s
195 }
196 crate::RemotePathCommitOptional::Mock { name } => format!("remote=mock,name={name}"),
197 }
198}