Skip to content

Commit 0eafbfc

Browse files
committed
Add 'spin-serde' crate
This collects some serde tools into a separate crate, avoiding some inter-crate dependencies. Signed-off-by: Lann Martin <[email protected]>
1 parent 297078b commit 0eafbfc

File tree

14 files changed

+195
-161
lines changed

14 files changed

+195
-161
lines changed

Cargo.lock

Lines changed: 25 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/app/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ edition = { workspace = true }
77
[dependencies]
88
anyhow = "1.0"
99
async-trait = "0.1"
10-
base64 = "0.21.3"
1110
ouroboros = "0.18.0"
1211
serde = { version = "1.0", features = ["derive"] }
1312
serde_json = "1.0"
1413
spin-core = { path = "../core" }
14+
spin-serde = { path = "../serde" }
1515
thiserror = "1.0"

crates/app/src/locked.rs

Lines changed: 2 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::path::PathBuf;
44

55
use serde::{Deserialize, Serialize};
66
use serde_json::Value;
7+
use spin_serde::FixedVersion;
78

89
use crate::{metadata::MetadataExt, values::ValuesMap};
910

@@ -120,7 +121,7 @@ pub struct ContentRef {
120121
#[serde(
121122
default,
122123
skip_serializing_if = "Option::is_none",
123-
with = "serde_base64"
124+
with = "spin_serde::base64"
124125
)]
125126
pub inline: Option<Vec<u8>>,
126127
/// If set, the content must have the given SHA-256 digest.
@@ -149,54 +150,3 @@ pub struct Variable {
149150
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
150151
pub secret: bool,
151152
}
152-
153-
/// FixedVersion represents a schema version field with a const value.
154-
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
155-
#[serde(into = "usize", try_from = "usize")]
156-
pub struct FixedVersion<const V: usize>;
157-
158-
impl<const V: usize> From<FixedVersion<V>> for usize {
159-
fn from(_: FixedVersion<V>) -> usize {
160-
V
161-
}
162-
}
163-
164-
impl<const V: usize> TryFrom<usize> for FixedVersion<V> {
165-
type Error = String;
166-
167-
fn try_from(value: usize) -> Result<Self, Self::Error> {
168-
if value != V {
169-
return Err(format!("invalid version {} != {}", value, V));
170-
}
171-
Ok(Self)
172-
}
173-
}
174-
175-
mod serde_base64 {
176-
use std::borrow::Cow;
177-
178-
use base64::{engine::GeneralPurpose, prelude::BASE64_STANDARD_NO_PAD, Engine};
179-
use serde::{de, Deserialize, Deserializer, Serializer};
180-
181-
const BASE64: GeneralPurpose = BASE64_STANDARD_NO_PAD;
182-
183-
pub fn serialize<S>(bytes: &Option<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error>
184-
where
185-
S: Serializer,
186-
{
187-
match bytes {
188-
Some(bytes) => serializer.serialize_str(&BASE64.encode(bytes)),
189-
None => serializer.serialize_none(),
190-
}
191-
}
192-
193-
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
194-
where
195-
D: Deserializer<'de>,
196-
{
197-
match Option::<Cow<str>>::deserialize(deserializer)? {
198-
Some(s) => Ok(Some(BASE64.decode(s.as_ref()).map_err(de::Error::custom)?)),
199-
None => Ok(None),
200-
}
201-
}
202-
}

crates/manifest/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ anyhow = "1.0.75"
99
indexmap = { version = "1", features = ["serde"] }
1010
outbound-http = { path = "../outbound-http" }
1111
serde = { version = "1.0", features = ["derive"] }
12-
spin-app = { path = "../app" }
12+
spin-serde = { path = "../serde" }
1313
thiserror = "1"
1414
toml = { version = "0.8.0", features = ["preserve_order"] }
1515

crates/manifest/src/compat.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ fn component_id_from_string(id: String) -> Result<v2::KebabId, Error> {
102102
.map_err(|err: String| Error::InvalidID { id, reason: err })
103103
}
104104

105-
fn id_from_string<const DELIM: char>(id: String) -> Result<v2::Id<DELIM>, Error> {
105+
fn id_from_string<const DELIM: char>(id: String) -> Result<spin_serde::id::Id<DELIM>, Error> {
106106
id.clone()
107107
.try_into()
108108
.map_err(|err: String| Error::InvalidID { id, reason: err })

crates/manifest/src/schema/mod.rs

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,3 @@ pub(crate) struct VersionProbe {
1616
#[serde(alias = "spin_version")]
1717
pub spin_manifest_version: toml::Value,
1818
}
19-
20-
/// Fixed schema version 1; (de)serializes as string "1".
21-
#[derive(Clone, Debug, Default, Deserialize)]
22-
#[serde(into = "String", try_from = "String")]
23-
pub struct FixedStringVersion<const V: usize>;
24-
25-
impl<const V: usize> From<FixedStringVersion<V>> for String {
26-
fn from(_: FixedStringVersion<V>) -> String {
27-
V.to_string()
28-
}
29-
}
30-
31-
impl<const V: usize> TryFrom<String> for FixedStringVersion<V> {
32-
type Error = String;
33-
34-
fn try_from(value: String) -> Result<Self, Self::Error> {
35-
if value.parse() != Ok(V) {
36-
return Err(format!("invalid version {value:?} != \"{V}\""));
37-
}
38-
Ok(Self)
39-
}
40-
}

crates/manifest/src/schema/v1.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use serde::Deserialize;
22

3+
use spin_serde::FixedStringVersion;
4+
35
pub use super::common::{
46
ComponentBuildConfig as ComponentBuildConfigV1, ComponentSource as ComponentSourceV1,
57
Variable as VariableV1, WasiFilesMount as WasiFilesMountV1,
68
};
7-
use super::FixedStringVersion;
89

910
type Map<K, V> = indexmap::IndexMap<K, V>;
1011

crates/manifest/src/schema/v2.rs

Lines changed: 2 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use serde::{Deserialize, Serialize};
2-
use spin_app::locked::FixedVersion;
2+
use spin_serde::FixedVersion;
3+
pub use spin_serde::{KebabId, SnakeId};
34

45
pub use super::common::{ComponentBuildConfig, ComponentSource, Variable, WasiFilesMount};
56

@@ -115,70 +116,6 @@ pub struct Component {
115116
pub build: Option<ComponentBuildConfig>,
116117
}
117118

118-
/// A "kebab-case" identifier.
119-
pub type KebabId = Id<'-'>;
120-
121-
/// A "snake_case" identifier.
122-
pub type SnakeId = Id<'_'>;
123-
124-
/// An ID is a non-empty string containing one or more component model
125-
/// `word`s separated by a delimiter char.
126-
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
127-
#[serde(into = "String", try_from = "String")]
128-
pub struct Id<const DELIM: char>(String);
129-
130-
impl<const DELIM: char> std::fmt::Display for Id<DELIM> {
131-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132-
write!(f, "{}", self.0)
133-
}
134-
}
135-
136-
impl<const DELIM: char> AsRef<str> for Id<DELIM> {
137-
fn as_ref(&self) -> &str {
138-
&self.0
139-
}
140-
}
141-
142-
impl<const DELIM: char> From<Id<DELIM>> for String {
143-
fn from(value: Id<DELIM>) -> Self {
144-
value.0
145-
}
146-
}
147-
148-
impl<const DELIM: char> TryFrom<String> for Id<DELIM> {
149-
type Error = String;
150-
151-
fn try_from(id: String) -> Result<Self, Self::Error> {
152-
if id.is_empty() {
153-
return Err("empty".into());
154-
}
155-
for word in id.split(DELIM) {
156-
if word.is_empty() {
157-
return Err(format!("{DELIM:?}-separated words must not be empty"));
158-
}
159-
let mut chars = word.chars();
160-
let first = chars.next().unwrap();
161-
if !first.is_ascii_alphabetic() {
162-
return Err(format!(
163-
"{DELIM:?}-separated words must start with an ASCII letter; got {first:?}"
164-
));
165-
}
166-
let word_is_uppercase = first.is_ascii_uppercase();
167-
for ch in chars {
168-
if ch.is_ascii_digit() {
169-
} else if !ch.is_ascii_alphanumeric() {
170-
return Err(format!(
171-
"{DELIM:?}-separated words may only contain alphanumeric ASCII; got {ch:?}"
172-
));
173-
} else if ch.is_ascii_uppercase() != word_is_uppercase {
174-
return Err(format!("{DELIM:?}-separated words must be all lowercase or all UPPERCASE; got {word:?}"));
175-
}
176-
}
177-
}
178-
Ok(Self(id))
179-
}
180-
}
181-
182119
mod one_or_many {
183120
use serde::{Deserialize, Deserializer, Serialize, Serializer};
184121

crates/serde/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "spin-serde"
3+
version = { workspace = true }
4+
authors = { workspace = true }
5+
edition = { workspace = true }
6+
7+
[dependencies]
8+
base64 = "0.21.4"
9+
serde = "1.0.189"

0 commit comments

Comments
 (0)