Ability to create a new resource and slightly improved editor view of data
This commit is contained in:
parent
b643fceca3
commit
0bae05342f
7 changed files with 153 additions and 11 deletions
|
@ -66,7 +66,7 @@ pub struct SaveSettings {
|
||||||
pub save: bool,
|
pub save: bool,
|
||||||
|
|
||||||
/// Save behaviour when encountering a collision
|
/// Save behaviour when encountering a collision
|
||||||
#[arg(long, short = 'c', default_value = "overwrite-single-skip-multiple")]
|
#[arg(long, default_value = "overwrite-single-skip-multiple")]
|
||||||
pub collision_handling: CollisionHandling,
|
pub collision_handling: CollisionHandling,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,6 +100,9 @@ pub enum Command {
|
||||||
save: SaveSettings,
|
save: SaveSettings,
|
||||||
/// The resource to query for and edit
|
/// The resource to query for and edit
|
||||||
resource: String,
|
resource: String,
|
||||||
|
/// Create a blank resource with RESOURCE as the subject if the query fails
|
||||||
|
#[arg(long, short = 'c')]
|
||||||
|
create: bool,
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
server_reload: ServerReloadOptions,
|
server_reload: ServerReloadOptions,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,18 +1,31 @@
|
||||||
use crate::args_parser::SaveSettings;
|
use crate::args_parser::SaveSettings;
|
||||||
use crate::editor::open_in_editor::open_resource_in_editor;
|
use crate::editor::open_in_editor::open_resource_in_editor;
|
||||||
use crate::schema::lookup_handler::LookupHandler;
|
use crate::schema::lookup_handler::LookupHandler;
|
||||||
|
use crate::schema::resource::Resource;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tracing::info;
|
use tracing::{error, info};
|
||||||
|
|
||||||
pub fn editor(database_path: PathBuf, save_settings: SaveSettings, resource: String) {
|
pub fn editor(database_path: PathBuf, save_settings: SaveSettings, resource: String, create: bool) {
|
||||||
let resources = LookupHandler::load(&database_path).unwrap();
|
let resources = LookupHandler::load(&database_path).unwrap();
|
||||||
let (index, resource) = resources
|
let (index, resource) = match resources.lookup_with_index(resource.as_str()) {
|
||||||
.lookup_with_index(resource.as_str())
|
None => {
|
||||||
.expect("Couldn't find a resource for that query");
|
if create {
|
||||||
|
(None, Resource::new(resource))
|
||||||
|
} else {
|
||||||
|
error!("Couldn't find a resource for the query \"{resource}\"");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some((index, resource)) => (Some(index), resource.clone()),
|
||||||
|
};
|
||||||
let resource = open_resource_in_editor(resource).unwrap();
|
let resource = open_resource_in_editor(resource).unwrap();
|
||||||
if save_settings.save {
|
if save_settings.save {
|
||||||
let mut resources = resources.into_inner();
|
let mut resources = resources.into_inner();
|
||||||
resources.0[index] = resource;
|
if let Some(index) = index {
|
||||||
|
resources.0[index] = resource;
|
||||||
|
} else {
|
||||||
|
resources.0.push(resource);
|
||||||
|
}
|
||||||
resources.save(database_path).unwrap();
|
resources.save(database_path).unwrap();
|
||||||
} else {
|
} else {
|
||||||
info!("To save edits, run this command with the -s flag")
|
info!("To save edits, run this command with the -s flag")
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::schema::resource::Resource;
|
use crate::schema::resource::Resource;
|
||||||
|
use crate::schema::resource_complete_serialisation::ResourceComplete;
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
use std::io::{Read, Seek, SeekFrom, Write};
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
@ -80,9 +81,11 @@ pub enum ResourceEditingError {
|
||||||
|
|
||||||
/// Opens the provided `resource` in the system editor and returns the edited version or an error if something goes wrong.
|
/// Opens the provided `resource` in the system editor and returns the edited version or an error if something goes wrong.
|
||||||
#[instrument(skip(resource))]
|
#[instrument(skip(resource))]
|
||||||
pub fn open_resource_in_editor(resource: &Resource) -> Result<Resource, ResourceEditingError> {
|
pub fn open_resource_in_editor(resource: Resource) -> Result<Resource, ResourceEditingError> {
|
||||||
use ResourceEditingError::*;
|
use ResourceEditingError::*;
|
||||||
let printed = serde_json::to_string_pretty(resource).map_err(PrettyPrintFailed)?;
|
let resource = ResourceComplete::from_resource(resource);
|
||||||
|
let printed = serde_json::to_string_pretty(&resource).map_err(PrettyPrintFailed)?;
|
||||||
let edited = spawn_editor(printed)?;
|
let edited = spawn_editor(printed)?;
|
||||||
serde_json::from_str(edited.as_str()).map_err(ParseFailed)
|
let parsed: ResourceComplete = serde_json::from_str(edited.as_str()).map_err(ParseFailed)?;
|
||||||
|
Ok(parsed.into_resource())
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,9 +57,10 @@ fn main() {
|
||||||
Command::Editor {
|
Command::Editor {
|
||||||
save,
|
save,
|
||||||
resource,
|
resource,
|
||||||
|
create,
|
||||||
server_reload,
|
server_reload,
|
||||||
} => {
|
} => {
|
||||||
editor(data_paths.database_path, save, resource);
|
editor(data_paths.database_path, save, resource, create);
|
||||||
|
|
||||||
reload(data_paths.pid_file_path, server_reload);
|
reload(data_paths.pid_file_path, server_reload);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
pub mod lookup_handler;
|
pub mod lookup_handler;
|
||||||
pub mod resource;
|
pub mod resource;
|
||||||
|
pub mod resource_complete_serialisation;
|
||||||
pub mod resource_list;
|
pub mod resource_list;
|
||||||
|
|
|
@ -23,6 +23,15 @@ impl Display for Resource {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Resource {
|
impl Resource {
|
||||||
|
pub fn new(subject: String) -> Self {
|
||||||
|
Self {
|
||||||
|
subject,
|
||||||
|
aliases: None,
|
||||||
|
properties: None,
|
||||||
|
links: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the aliases of the given record. If the `aliases` field is
|
/// Returns the aliases of the given record. If the `aliases` field is
|
||||||
/// entirely missing, returns &[].
|
/// entirely missing, returns &[].
|
||||||
pub fn keys(&self) -> impl Iterator<Item = &String> {
|
pub fn keys(&self) -> impl Iterator<Item = &String> {
|
||||||
|
|
112
src/schema/resource_complete_serialisation.rs
Normal file
112
src/schema/resource_complete_serialisation.rs
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
use crate::schema::resource::{Link, Resource};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
/// Same as `Resource`, but doesn't have any of the skip_serializing_if fields and options set so the structure is more friendly to editing through the `editor` command
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct ResourceComplete {
|
||||||
|
subject: String,
|
||||||
|
aliases: Vec<String>,
|
||||||
|
properties: HashMap<String, Option<String>>,
|
||||||
|
links: Vec<LinkComplete>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResourceComplete {
|
||||||
|
pub fn from_resource(value: Resource) -> Self {
|
||||||
|
let Resource {
|
||||||
|
subject,
|
||||||
|
aliases,
|
||||||
|
properties,
|
||||||
|
links,
|
||||||
|
} = value;
|
||||||
|
Self {
|
||||||
|
subject,
|
||||||
|
aliases: aliases.unwrap_or_default(),
|
||||||
|
properties: properties.unwrap_or_default(),
|
||||||
|
links: links
|
||||||
|
.map(|vec| vec.into_iter().map(LinkComplete::from_link).collect())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_resource(self) -> Resource {
|
||||||
|
let ResourceComplete {
|
||||||
|
subject,
|
||||||
|
aliases,
|
||||||
|
properties,
|
||||||
|
links,
|
||||||
|
} = self;
|
||||||
|
Resource {
|
||||||
|
subject,
|
||||||
|
aliases: if aliases.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(aliases)
|
||||||
|
},
|
||||||
|
properties: if properties.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(properties)
|
||||||
|
},
|
||||||
|
links: if links.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(links.into_iter().map(LinkComplete::into_link).collect())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Same as `Link`, but doesn't have any of the skip_serializing_if fields set so the structure is more friendly to editing through the `editor` command
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct LinkComplete {
|
||||||
|
rel: String,
|
||||||
|
media_type: Option<String>,
|
||||||
|
href: Option<String>,
|
||||||
|
titles: HashMap<String, String>,
|
||||||
|
properties: HashMap<String, Option<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LinkComplete {
|
||||||
|
fn from_link(value: Link) -> Self {
|
||||||
|
let Link {
|
||||||
|
rel,
|
||||||
|
media_type,
|
||||||
|
href,
|
||||||
|
titles,
|
||||||
|
properties,
|
||||||
|
} = value;
|
||||||
|
Self {
|
||||||
|
rel,
|
||||||
|
media_type,
|
||||||
|
href,
|
||||||
|
titles: titles.unwrap_or_default(),
|
||||||
|
properties: properties.unwrap_or_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_link(self) -> Link {
|
||||||
|
let LinkComplete {
|
||||||
|
rel,
|
||||||
|
media_type,
|
||||||
|
href,
|
||||||
|
titles,
|
||||||
|
properties,
|
||||||
|
} = self;
|
||||||
|
Link {
|
||||||
|
rel,
|
||||||
|
media_type,
|
||||||
|
href,
|
||||||
|
titles: if titles.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(titles)
|
||||||
|
},
|
||||||
|
properties: if properties.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(properties)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue