kcode_rust_libs_v2/
lib.rs1use std::error::Error as StdError;
4use std::fmt;
5use std::path::Path;
6
7use kcode_rust_library_repository::Repository;
8use kcode_rust_source::Source;
9
10pub use kcode_rust_source::File;
11
12pub struct Error(String);
14
15pub type Result<T> = std::result::Result<T, Error>;
17
18pub struct Lib {
24 pub files: Vec<File>,
26 repository: Repository,
27 name: String,
28 token: Secret,
29}
30
31pub fn create(
33 rust_libs_root: impl AsRef<Path>,
34 name: &str,
35 crates_io_registry_token: impl AsRef<str>,
36) -> Result<Lib> {
37 let token = Secret::new(crates_io_registry_token.as_ref())?;
38 let source = initial_source(name)?;
39 let repository = kcode_rust_library_repository::create(rust_libs_root, name, &source)
40 .map_err(Error::leaf)?;
41 Ok(Lib::new(repository, name, token))
42}
43
44pub fn open(
46 rust_libs_root: impl AsRef<Path>,
47 name: &str,
48 crates_io_registry_token: impl AsRef<str>,
49) -> Result<Lib> {
50 let token = Secret::new(crates_io_registry_token.as_ref())?;
51 let repository =
52 kcode_rust_library_repository::open(rust_libs_root, name).map_err(Error::leaf)?;
53 Ok(Lib::new(repository, name, token))
54}
55
56pub fn docs(rust_libs_root: impl AsRef<Path>, name: &str) -> Result<(String, String)> {
58 kcode_rust_library_repository::docs(rust_libs_root, name).map_err(Error::leaf)
59}
60
61impl Lib {
62 fn new(repository: Repository, name: &str, token: Secret) -> Self {
63 let files = repository.source().files().to_vec();
64 Self {
65 files,
66 repository,
67 name: name.to_owned(),
68 token,
69 }
70 }
71
72 pub fn write(&mut self) -> Result<()> {
74 let source = Source::validate(&self.files, &self.name).map_err(Error::leaf)?;
75 self.repository.replace(&source).map_err(Error::leaf)?;
76 self.files = source.files().to_vec();
77 Ok(())
78 }
79
80 pub fn check(&self) -> Result<()> {
82 let source = Source::validate(&self.files, &self.name).map_err(Error::leaf)?;
83 kcode_rust_library_toolchain::check(&source).map_err(Error::leaf)
84 }
85
86 pub fn publish(&self) -> Result<()> {
88 let source = Source::validate(&self.files, &self.name).map_err(Error::leaf)?;
89 kcode_rust_library_toolchain::publish(&source, self.token.expose()).map_err(Error::leaf)
90 }
91}
92
93impl Error {
94 fn new(category: &str, message: impl fmt::Display) -> Self {
95 Self(format!("{category}: {message}"))
96 }
97
98 fn leaf(error: impl fmt::Display) -> Self {
99 Self(error.to_string())
100 }
101}
102
103impl fmt::Display for Error {
104 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
105 formatter.write_str(&self.0)
106 }
107}
108
109impl fmt::Debug for Error {
110 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
111 formatter.debug_tuple("Error").field(&self.0).finish()
112 }
113}
114
115impl StdError for Error {}
116
117impl fmt::Debug for Lib {
118 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
119 formatter
120 .debug_struct("Lib")
121 .field("files", &self.files)
122 .field("repository", &"[PRIVATE]")
123 .field("name", &self.name)
124 .field("token", &"[REDACTED]")
125 .finish()
126 }
127}
128
129struct Secret(String);
130
131impl Secret {
132 fn new(value: &str) -> Result<Self> {
133 let value = value.trim();
134 if value.is_empty() {
135 return Err(Error::new(
136 "invalid_token",
137 "the crates.io registry token is empty",
138 ));
139 }
140 Ok(Self(value.to_owned()))
141 }
142
143 fn expose(&self) -> &str {
144 &self.0
145 }
146}
147
148fn initial_source(name: &str) -> Result<Source> {
149 Source::validate(
150 &[
151 File {
152 path: "Cargo.toml".to_owned(),
153 contents: format!(
154 "[package]\nname = \"{name}\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n[dependencies]\n"
155 ),
156 },
157 File {
158 path: "Documentation.md".to_owned(),
159 contents: String::new(),
160 },
161 File {
162 path: "src/lib.rs".to_owned(),
163 contents: String::new(),
164 },
165 ],
166 name,
167 )
168 .map_err(Error::leaf)
169}