Skip to main content

kcode_rust_bins/
lib.rs

1//! Minimal managed lifecycle for small object-published Rust binaries.
2
3mod execution;
4mod model;
5mod protocol;
6mod publication;
7mod repository;
8mod sandbox;
9
10use std::fmt;
11use std::path::{Path, PathBuf};
12use std::time::Duration;
13
14pub use model::{Error, File, Result, RunRequest, RunResult, RustBinInput, RustBinOutput};
15use model::{ValidSource, validate_name, validate_source};
16pub use protocol::{read_input, write_output};
17
18/// Default maximum running time for one binary call.
19pub const DEFAULT_RUN_TIMEOUT: Duration = Duration::from_secs(2 * 60);
20
21/// One complete editable managed-binary source snapshot.
22pub struct Lib {
23    /// Every useful UTF-8 source file, canonically sorted after open or write.
24    pub files: Vec<File>,
25    repository: PathBuf,
26    name: String,
27    identity: String,
28    publications: PathBuf,
29}
30
31/// Creates a minimal Rust 2024 binary and returns its complete source.
32pub fn create(
33    rust_bins_root: impl AsRef<Path>,
34    publications_root: impl AsRef<Path>,
35    name: &str,
36) -> Result<Lib> {
37    validate_name(name)?;
38    let source = initial_source(name)?;
39    let snapshot = repository::create(rust_bins_root.as_ref(), name, &source)?;
40    Ok(Lib {
41        files: snapshot.files,
42        repository: snapshot.repository,
43        name: name.to_owned(),
44        identity: snapshot.identity,
45        publications: publications_root.as_ref().to_path_buf(),
46    })
47}
48
49/// Opens an existing binary, migrating a valid legacy flat repository when needed.
50pub fn open(
51    rust_bins_root: impl AsRef<Path>,
52    publications_root: impl AsRef<Path>,
53    name: &str,
54) -> Result<Lib> {
55    validate_name(name)?;
56    let snapshot = repository::open(rust_bins_root.as_ref(), name)?;
57    Ok(Lib {
58        files: snapshot.files,
59        repository: snapshot.repository,
60        name: name.to_owned(),
61        identity: snapshot.identity,
62        publications: publications_root.as_ref().to_path_buf(),
63    })
64}
65
66/// Returns the package version and root `Documentation.md`, migrating when needed.
67pub fn docs(rust_bins_root: impl AsRef<Path>, name: &str) -> Result<(String, String)> {
68    validate_name(name)?;
69    repository::docs(rust_bins_root.as_ref(), name)
70}
71
72/// Resolves and executes a locally published binary.
73///
74/// `version_selector` follows the Web-library convention: `v1.2.3` is exact,
75/// while unprefixed and abbreviated selectors are Cargo-compatible
76/// requirements. `None` uses [`DEFAULT_RUN_TIMEOUT`].
77pub fn run(
78    publications_root: impl AsRef<Path>,
79    name: &str,
80    version_selector: &str,
81    request: RunRequest,
82    timeout: Option<Duration>,
83) -> Result<RunResult> {
84    validate_name(name)?;
85    let publication = publication::resolve(publications_root.as_ref(), name, version_selector)?;
86    execution::run(
87        &publication.executable,
88        publication.version.to_string(),
89        request,
90        timeout.unwrap_or(DEFAULT_RUN_TIMEOUT),
91    )
92}
93
94impl Lib {
95    /// Atomically commits `files` as the complete source if this snapshot is current.
96    pub fn write(&mut self) -> Result<()> {
97        let source = validate_source(&self.files, &self.name)?;
98        let snapshot = repository::replace(&self.repository, &self.identity, &source)?;
99        self.files = snapshot.files;
100        self.identity = snapshot.identity;
101        Ok(())
102    }
103
104    /// Validates and statically builds exactly the current in-memory source.
105    pub fn check(&self) -> Result<()> {
106        let source = validate_source(&self.files, &self.name)?;
107        sandbox::check(&source, &self.name)
108    }
109
110    /// Validates once and atomically publishes this exact name and version.
111    pub fn publish(&self) -> Result<()> {
112        let source = validate_source(&self.files, &self.name)?;
113        let executable = sandbox::build(&source, &self.name)?;
114        publication::publish(&self.publications, &self.name, &source.version, &executable)
115    }
116}
117
118impl fmt::Debug for Lib {
119    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
120        formatter
121            .debug_struct("Lib")
122            .field("files", &self.files)
123            .field("repository", &self.repository)
124            .field("name", &self.name)
125            .field("identity", &"[PRIVATE]")
126            .field("publications", &self.publications)
127            .finish()
128    }
129}
130
131fn initial_source(name: &str) -> Result<ValidSource> {
132    validate_source(
133        &[
134            File {
135                path: "Cargo.toml".to_owned(),
136                contents: format!(
137                    "[package]\nname = \"{name}\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n[dependencies]\n"
138                ),
139            },
140            File {
141                path: "Documentation.md".to_owned(),
142                contents: String::new(),
143            },
144            File {
145                path: "src/main.rs".to_owned(),
146                contents: "fn main() {}\n".to_owned(),
147            },
148        ],
149        name,
150    )
151}