Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
|
|
Hey @rbro112, can you please resolve or mark as irrelevant all of the Seer code review comments before I review in more depth? |
|
@szokeasaurusrex this is ready for review. I'm ignoring the Danger comment as the last item is |
src/api/data_types/snapshots.rs
Outdated
| const RESERVED_KEYS: &[&str] = &["image_file_name", "width", "height"]; | ||
|
|
||
| impl Serialize for ImageMetadata { | ||
| fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { |
There was a problem hiding this comment.
My rust knowledge isn't robust enough, but basically this is what I believe should be equivalent of the typescript "splat". Essentially what we want to do here is always override "image_file_name", "width", "height" if the user has provided those values. These are first-class fields we will set (will be documented) and users cannot be allowed to override those.
In typescript, this would be how I'd override:
const mergedMetadata = {
...extras, // User's fields
width: ourWidth,
height: ourHeight
image_file_name: ourImageFileName,
}
It seems rust doesn't have an "override"/"splat" functionality like this, so the serializer/reserved keys is our workaround. Open to more elegant solutions here nonetheless.
3a87a25 to
93bd92d
Compare
szokeasaurusrex
left a comment
There was a problem hiding this comment.
Looks good to me, I left some minor optional suggestions, please address prior to merge!
src/commands/build/snapshots.rs
Outdated
| fn read_sidecar_metadata(image_path: &Path) -> HashMap<String, serde_json::Value> { | ||
| let sidecar_path = image_path.with_extension("json"); | ||
| if !sidecar_path.is_file() { | ||
| return HashMap::new(); | ||
| } | ||
|
|
||
| debug!("Reading sidecar metadata: {}", sidecar_path.display()); | ||
| let contents = match fs::read_to_string(&sidecar_path) { | ||
| Ok(c) => c, | ||
| Err(err) => { | ||
| warn!( | ||
| "Failed to read sidecar file {}: {err}", | ||
| sidecar_path.display() | ||
| ); | ||
| return HashMap::new(); | ||
| } | ||
| }; | ||
|
|
||
| match serde_json::from_str(&contents) { | ||
| Ok(map) => map, | ||
| Err(err) => { | ||
| warn!( | ||
| "Failed to parse sidecar file {}: {err}", | ||
| sidecar_path.display() | ||
| ); | ||
| HashMap::new() | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
l: A few minor suggestions in one.
| fn read_sidecar_metadata(image_path: &Path) -> HashMap<String, serde_json::Value> { | |
| let sidecar_path = image_path.with_extension("json"); | |
| if !sidecar_path.is_file() { | |
| return HashMap::new(); | |
| } | |
| debug!("Reading sidecar metadata: {}", sidecar_path.display()); | |
| let contents = match fs::read_to_string(&sidecar_path) { | |
| Ok(c) => c, | |
| Err(err) => { | |
| warn!( | |
| "Failed to read sidecar file {}: {err}", | |
| sidecar_path.display() | |
| ); | |
| return HashMap::new(); | |
| } | |
| }; | |
| match serde_json::from_str(&contents) { | |
| Ok(map) => map, | |
| Err(err) => { | |
| warn!( | |
| "Failed to parse sidecar file {}: {err}", | |
| sidecar_path.display() | |
| ); | |
| HashMap::new() | |
| } | |
| } | |
| } | |
| fn read_sidecar_metadata(image_path: &Path) -> Result<HashMap<String, Value>> { | |
| let sidecar_path = image_path.with_extension("json"); | |
| if !sidecar_path.is_file() { | |
| return Ok(HashMap::new()); | |
| } | |
| debug!("Reading sidecar metadata: {}", sidecar_path.display()); | |
| let sidecar_file = File::open(&sidecar_path) | |
| .with_context(|| format!("Failed to open sidecar file {}", sidecar_path.display()))?; | |
| serde_json::from_reader(BufReader::new(sidecar_file)).with_context(|| { | |
| format!( | |
| "Failed to read sidecar file {} as JSON", | |
| sidecar_path.display() | |
| ) | |
| }) | |
| } |
The notable changes in order of importance:
- Use
serde_json::from_readerto avoid reading the entire sidecar file to a string (should reduce memory usage for large sidecar files). - I'd return the error and handle it in the calling code; this allows you to use the
?operator in the function, and factor out common error handling code (see associated comment at the call site) - Style change: import
serde_json::Valueso you can reference it asValuedirectly, this is the style recommended for types in Rust's style guide.
You need to add the following imports at the beginning of the file, as well.
// std imports
use std::fs::{self, File};
use std::io::BufReader;
// crate imports
use serde_json::Value;
src/commands/build/snapshots.rs
Outdated
| .to_string_lossy() | ||
| .into_owned(); | ||
|
|
||
| let extra = read_sidecar_metadata(&image.path); |
There was a problem hiding this comment.
This comment is associated with item (2) in my previous comment
| let extra = read_sidecar_metadata(&image.path); | |
| let extra = read_sidecar_metadata(&image.path).unwrap_or_else(|err| { | |
| warn!("Error reading sidecar metadata, ignoring it instead: {err:#}"); | |
| HashMap::new() | |
| });``` |
93bd92d to
36a8cca
Compare

Description
First-class supported platforms (web library, iOS/Android in future) will be outputting a "sidecar" json file with each image containing custom metadata set by the framework and provided by the user. This will contain things like the "display name" among other items.
This updates our snapshots command to process these sidecar files if present and upload them as part of the image manifest data in the POST body.