cherenkov/model/index/
resolve.rs1use super::{
4 ModelDetails, ModelIndex, disk, reference,
5 selector::{self, Selector},
6};
7use anyhow::{Context, Result, ensure};
8use std::path::Path;
9
10#[derive(Default)]
12pub struct ResolveOptions<'a> {
13 pub name: Option<&'a str>,
15 pub revision: Option<&'a str>,
17 pub token: Option<&'a str>,
19 pub events: Option<&'a mut dyn FnMut(super::ModelEvent)>,
24}
25
26impl ResolveOptions<'_> {
27 fn apply(&self, selector: &mut Selector) -> Result<()> {
29 match selector {
30 Selector::Hub { revision, .. } => {
31 ensure!(
32 revision.is_none() || self.revision.is_none(),
33 "specify the revision with @revision or --revision, not both"
34 );
35
36 if let Some(value) = self.revision {
37 selector::validate_revision(value)?;
38
39 *revision = Some(value.to_owned());
40 }
41 }
42 Selector::Path(_) | Selector::Disk { .. } => {
43 ensure!(
44 self.revision.is_none() && self.token.is_none(),
45 "revision and token require an HF source"
46 );
47 }
48 Selector::Registered(_) | Selector::LocalRevision { .. } => {
49 ensure!(self.revision.is_none(), "revision requires a source URI");
50 }
51 }
52
53 Ok(())
54 }
55}
56
57impl ModelIndex {
58 pub fn select(&self, input: &Path, options: ResolveOptions<'_>) -> Result<ModelDetails> {
61 super::validate_name(options.name)?;
62
63 let mut selector = selector::parse(input)?;
64
65 options.apply(&mut selector)?;
66
67 if let Selector::Path(path) = &selector {
69 return self.add_local(path, options.name);
70 }
71
72 let input = input.to_str().context("invalid model selector")?;
73 let reference = match options.revision {
74 Some(revision) => format!("{input}@{revision}"),
75 None => input.to_owned(),
76 };
77 let existing = reference::find(&self.lock()?.value, &selector, &reference)?.cloned();
78
79 if let Some(model) = existing {
80 return self.name_existing(model, options.name);
81 }
82
83 match selector {
84 Selector::Hub { repo, revision } => {
85 let (source, description) = super::hub::inspect(
86 &repo,
87 revision.as_deref().unwrap_or("main"),
88 options.token,
89 options.events,
90 )?;
91 let id = self.register(source, description, options.name, None)?;
92
93 self.show(&id)
94 }
95 Selector::Disk {
96 store,
97 repo,
98 revision,
99 } => self.select_disk(&store, &repo, revision.as_deref(), options.name),
100 _ => anyhow::bail!("model {reference:?} is not registered"),
101 }
102 }
103
104 fn select_disk(
106 &self,
107 name: &str,
108 repo: &str,
109 revision: Option<&str>,
110 alias: Option<&str>,
111 ) -> Result<ModelDetails> {
112 let store = disk::registered(&self.lock()?.value, name)?.clone();
113
114 let path = store.locate(repo, revision)?;
115
116 if matches!(store.layout, super::DiskLayout::Directory)
117 && let Some(revision) = revision
118 {
119 ensure!(
120 reference::is_revision(revision)
121 && super::local::fingerprint(&path)?.starts_with(revision),
122 "disk model does not match the requested revision"
123 );
124 }
125
126 self.add_local(&path, alias)
127 }
128
129 fn name_existing(&self, model: super::ModelEntry, name: Option<&str>) -> Result<ModelDetails> {
131 if name.is_none() {
132 return self.show(&model.id);
133 }
134
135 let id = self.register(model.source, model.description, name, None)?;
136
137 self.show(&id)
138 }
139}