Skip to content

Commit ffd3461

Browse files
authored
docs: update docs for zerv struct (#31)
- update docs for zerv struct
1 parent 0757102 commit ffd3461

File tree

2 files changed

+667
-0
lines changed

2 files changed

+667
-0
lines changed

.dev/zerv-conversions.md

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
# Zerv Conversion System
2+
3+
## Overview
4+
5+
This document describes the conversion system between existing version objects (SemVerVersion, PEP440Version) and the Zerv universal format.
6+
7+
## Conversion Strategy
8+
9+
### From Existing Version Objects to Zerv
10+
11+
```rust
12+
// SemVer -> Zerv
13+
impl From<SemVerVersion> for Zerv {
14+
fn from(semver: SemVerVersion) -> Self {
15+
Zerv {
16+
format: ZervFormat {
17+
core: vec![
18+
Component::VarField("major"),
19+
Component::VarField("minor"),
20+
Component::VarField("patch")
21+
],
22+
extra_core: if semver.pre_release.is_some() {
23+
vec![Component::VarField("pre_release")]
24+
} else { vec![] },
25+
build: semver.build.iter().map(|s| Component::String(s.clone())).collect(),
26+
},
27+
vars: ZervVars {
28+
major: Some(semver.major),
29+
minor: Some(semver.minor),
30+
patch: Some(semver.patch),
31+
pre_release: semver.pre_release.map(|pr| PreReleaseVar {
32+
label: pr.label,
33+
number: pr.number
34+
}),
35+
..Default::default()
36+
},
37+
}
38+
}
39+
}
40+
41+
// PEP440 -> Zerv
42+
impl From<PEP440Version> for Zerv {
43+
fn from(pep440: PEP440Version) -> Self {
44+
let mut extra_core = vec![];
45+
if pep440.pre_release.is_some() {
46+
extra_core.push(Component::VarField("pre_release"));
47+
}
48+
if pep440.post.is_some() {
49+
extra_core.push(Component::VarField("post"));
50+
}
51+
if pep440.dev.is_some() {
52+
extra_core.push(Component::VarField("dev"));
53+
}
54+
55+
Zerv {
56+
format: ZervFormat {
57+
core: vec![
58+
Component::VarField("major"),
59+
Component::VarField("minor"),
60+
Component::VarField("patch")
61+
],
62+
extra_core,
63+
build: pep440.local.iter().map(|s| Component::String(s.clone())).collect(),
64+
},
65+
vars: ZervVars {
66+
major: Some(pep440.major),
67+
minor: Some(pep440.minor),
68+
patch: Some(pep440.patch),
69+
epoch: pep440.epoch,
70+
pre_release: pep440.pre_release.map(|pr| PreReleaseVar {
71+
label: pr.label,
72+
number: pr.number
73+
}),
74+
post: pep440.post,
75+
dev: pep440.dev,
76+
..Default::default()
77+
},
78+
}
79+
}
80+
}
81+
```
82+
83+
### From Zerv to Existing Version Objects
84+
85+
```rust
86+
// Zerv -> SemVer
87+
impl TryFrom<Zerv> for SemVerVersion {
88+
type Error = ConversionError;
89+
90+
fn try_from(zerv: Zerv) -> Result<Self, Self::Error> {
91+
// Validate format matches SemVer structure
92+
if !is_semver_compatible(&zerv.format) {
93+
return Err(ConversionError::IncompatibleFormat);
94+
}
95+
96+
Ok(SemVerVersion {
97+
major: zerv.vars.major.ok_or(ConversionError::MissingField("major"))?,
98+
minor: zerv.vars.minor.ok_or(ConversionError::MissingField("minor"))?,
99+
patch: zerv.vars.patch.ok_or(ConversionError::MissingField("patch"))?,
100+
pre_release: zerv.vars.pre_release.map(|pr| SemVerPreRelease {
101+
label: pr.label,
102+
number: pr.number
103+
}),
104+
build: extract_build_strings(&zerv.format.build, &zerv.vars)?,
105+
})
106+
}
107+
}
108+
109+
// Zerv -> PEP440
110+
impl TryFrom<Zerv> for PEP440Version {
111+
type Error = ConversionError;
112+
113+
fn try_from(zerv: Zerv) -> Result<Self, Self::Error> {
114+
// Validate format matches PEP440 structure
115+
if !is_pep440_compatible(&zerv.format) {
116+
return Err(ConversionError::IncompatibleFormat);
117+
}
118+
119+
Ok(PEP440Version {
120+
epoch: zerv.vars.epoch,
121+
major: zerv.vars.major.ok_or(ConversionError::MissingField("major"))?,
122+
minor: zerv.vars.minor.ok_or(ConversionError::MissingField("minor"))?,
123+
patch: zerv.vars.patch.ok_or(ConversionError::MissingField("patch"))?,
124+
pre_release: zerv.vars.pre_release.map(|pr| PEP440PreRelease {
125+
label: pr.label,
126+
number: pr.number
127+
}),
128+
post: zerv.vars.post,
129+
dev: zerv.vars.dev,
130+
local: extract_build_strings(&zerv.format.build, &zerv.vars)?,
131+
})
132+
}
133+
}
134+
```
135+
136+
## Helper Functions
137+
138+
```rust
139+
fn is_semver_compatible(format: &ZervFormat) -> bool {
140+
// Check if format structure matches SemVer expectations
141+
format.core.len() == 3 &&
142+
matches!(format.core[0], Component::VarField(ref s) if s == "major") &&
143+
matches!(format.core[1], Component::VarField(ref s) if s == "minor") &&
144+
matches!(format.core[2], Component::VarField(ref s) if s == "patch")
145+
}
146+
147+
fn is_pep440_compatible(format: &ZervFormat) -> bool {
148+
// Check if format structure matches PEP440 expectations
149+
format.core.len() >= 3 &&
150+
matches!(format.core[0], Component::VarField(ref s) if s == "major") &&
151+
matches!(format.core[1], Component::VarField(ref s) if s == "minor") &&
152+
matches!(format.core[2], Component::VarField(ref s) if s == "patch")
153+
}
154+
155+
fn extract_build_strings(build_components: &[Component], vars: &ZervVars) -> Result<Vec<String>, ConversionError> {
156+
build_components.iter()
157+
.map(|comp| match comp {
158+
Component::String(s) => Ok(s.clone()),
159+
Component::Integer(i) => Ok(i.to_string()),
160+
Component::VarField(field) => resolve_var_field(field, vars),
161+
_ => Err(ConversionError::UnsupportedComponent)
162+
})
163+
.collect()
164+
}
165+
166+
fn resolve_var_field(field: &str, vars: &ZervVars) -> Result<String, ConversionError> {
167+
match field {
168+
"major" => vars.major.map(|v| v.to_string()).ok_or(ConversionError::MissingField("major")),
169+
"minor" => vars.minor.map(|v| v.to_string()).ok_or(ConversionError::MissingField("minor")),
170+
"patch" => vars.patch.map(|v| v.to_string()).ok_or(ConversionError::MissingField("patch")),
171+
"current_branch" => vars.current_branch.clone().ok_or(ConversionError::MissingField("current_branch")),
172+
"distance" => vars.distance.map(|v| v.to_string()).ok_or(ConversionError::MissingField("distance")),
173+
"current_commit_hash" => vars.current_commit_hash.clone().ok_or(ConversionError::MissingField("current_commit_hash")),
174+
_ => Err(ConversionError::UnsupportedField(field.to_string()))
175+
}
176+
}
177+
```
178+
179+
## Error Handling
180+
181+
```rust
182+
#[derive(Debug, thiserror::Error)]
183+
pub enum ConversionError {
184+
#[error("Incompatible format structure")]
185+
IncompatibleFormat,
186+
#[error("Missing required field: {0}")]
187+
MissingField(&'static str),
188+
#[error("Unsupported component type")]
189+
UnsupportedComponent,
190+
#[error("Unsupported field: {0}")]
191+
UnsupportedField(String),
192+
}
193+
```
194+
195+
## Usage Examples
196+
197+
```rust
198+
// Convert existing version to Zerv
199+
let semver = SemVerVersion::new(1, 2, 3);
200+
let zerv: Zerv = semver.into();
201+
202+
// Convert Zerv back to existing version
203+
let semver_back: SemVerVersion = zerv.try_into()?;
204+
205+
// Cross-format conversion via Zerv
206+
let pep440 = PEP440Version::new(1, 2, 3);
207+
let zerv: Zerv = pep440.into();
208+
let semver: SemVerVersion = zerv.try_into()?;
209+
```
210+
211+
## Benefits
212+
213+
1. **Lossless Conversion**: Preserves all semantic information
214+
2. **Type Safety**: Compile-time guarantees with proper error handling
215+
3. **Extensible**: Easy to add new version formats
216+
4. **Interoperability**: Seamless conversion between different version systems
217+
5. **Clean Separation**: Format structure separate from data values

0 commit comments

Comments
 (0)