Skip to content

Instantly share code, notes, and snippets.

@h-michael
Last active February 12, 2020 08:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save h-michael/deb0052a00456709bcb2f557c94bf083 to your computer and use it in GitHub Desktop.
Save h-michael/deb0052a00456709bcb2f557c94bf083 to your computer and use it in GitHub Desktop.
$ nvim --version
NVIM v0.5.0-366-g932949229
$ ra_lsp_server
rust-analyzer 759100f
```
[ INFO ] 2020-02-12T17:11:02Z+0900 ] .../h-michael/.local/share/nvim/runtime/lua/vim/lsp/rpc.lua:204 ] "Starting RPC client" { args = {}, cmd = "ra_lsp_server", extra = {}}
[ DEBUG ] 2020-02-12T17:11:02Z+0900 ] /home/h-michael/.local/share/nvim/runtime/lua/vim/lsp.lua:486 ] "LSP[rust_analyzer]" "initialize_params" { capabilities = { textDocument = { completion = { completionItem = { commitCharactersSupport = false, deprecatedSupport = false, documentationFormat = { "markdown", "plaintext" }, preselectSupport = false, snippetSupport = false }, completionItemKind = { valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 } }, contextSupport = false, dynamicRegistration = false }, documentHighlight = { dynamicRegistration = false }, documentSymbol = { dynamicRegistration = false, hierarchicalDocumentSymbolSupport = false, symbolKind = { valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 } } }, hover = { contentFormat = { "markdown", "plaintext" }, dynamicRegistration = false }, references = { dynamicRegistration = false }, signatureHelp = { dynamicRegistration = false, signatureInformation = { documentationFormat = { "markdown", "plaintext" } } }, synchronization = { didSave = true, dynamicRegistration = false, willSave = false, willSaveWaitUntil = false } }, workspace = { configuration = true } }, initializationOptions = {}, processId = 290032, rootPath = "/home/h-michael/go/src/github.com/h-michael/graphql-client/graphql_client", rootUri = "file:///home/h-michael/go/src/github.com/h-michael/graphql-client/graphql_client", trace = "off"}
[ DEBUG ] 2020-02-12T17:11:02Z+0900 ] .../h-michael/.local/share/nvim/runtime/lua/vim/lsp/rpc.lua:271 ] "rpc.send.payload" { id = 1, jsonrpc = "2.0", method = "initialize", params = { capabilities = { textDocument = { completion = { completionItem = { commitCharactersSupport = false, deprecatedSupport = false, documentationFormat = { "markdown", "plaintext" }, preselectSupport = false, snippetSupport = false }, completionItemKind = { valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 } }, contextSupport = false, dynamicRegistration = false }, documentHighlight = { dynamicRegistration = false }, documentSymbol = { dynamicRegistration = false, hierarchicalDocumentSymbolSupport = false, symbolKind = { valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 } } }, hover = { contentFormat = { "markdown", "plaintext" }, dynamicRegistration = false }, references = { dynamicRegistration = false }, signatureHelp = { dynamicRegistration = false, signatureInformation = { documentationFormat = { "markdown", "plaintext" } } }, synchronization = { didSave = true, dynamicRegistration = false, willSave = false, willSaveWaitUntil = false } }, workspace = { configuration = true } }, initializationOptions = {}, processId = 290032, rootPath = "/home/h-michael/go/src/github.com/h-michael/graphql-client/graphql_client", rootUri = "file:///home/h-michael/go/src/github.com/h-michael/graphql-client/graphql_client", trace = "off" }}
[ DEBUG ] 2020-02-12T17:11:02Z+0900 ] .../h-michael/.local/share/nvim/runtime/lua/vim/lsp/rpc.lua:351 ] "decoded" { id = 1, jsonrpc = "2.0", result = { capabilities = { callHierarchyProvider = true, codeActionProvider = true, codeLensProvider = { resolveProvider = true }, completionProvider = { triggerCharacters = { ":", "." } }, definitionProvider = true, documentFormattingProvider = true, documentHighlightProvider = true, documentOnTypeFormattingProvider = { firstTriggerCharacter = "=", moreTriggerCharacter = { ".", ">" } }, documentSymbolProvider = true, foldingRangeProvider = true, hoverProvider = true, implementationProvider = true, referencesProvider = true, renameProvider = { prepareProvider = true }, selectionRangeProvider = true, signatureHelpProvider = { triggerCharacters = { "(", "," } }, textDocumentSync = { change = 1, openClose = true, save = vim.empty_dict() }, typeDefinitionProvider = true, workspaceSymbolProvider = true } }}
[ DEBUG ] 2020-02-12T17:11:02Z+0900 ] .../h-michael/.local/share/nvim/runtime/lua/vim/lsp/rpc.lua:282 ] "rpc.notify" "initialized" { [true] = 6}
[ DEBUG ] 2020-02-12T17:11:02Z+0900 ] .../h-michael/.local/share/nvim/runtime/lua/vim/lsp/rpc.lua:271 ] "rpc.send.payload" { jsonrpc = "2.0", method = "initialized", params = { [true] = 6 }}
[ DEBUG ] 2020-02-12T17:11:02Z+0900 ] /home/h-michael/.local/share/nvim/runtime/lua/vim/lsp.lua:503 ] "LSP[rust_analyzer]" "server_capabilities" { callHierarchyProvider = true, codeActionProvider = true, codeLensProvider = { resolveProvider = true }, completionProvider = { triggerCharacters = { ":", "." } }, definitionProvider = true, documentFormattingProvider = true, documentHighlightProvider = true, documentOnTypeFormattingProvider = { firstTriggerCharacter = "=", moreTriggerCharacter = { ".", ">" } }, documentSymbolProvider = true, foldingRangeProvider = true, hoverProvider = true, implementationProvider = true, referencesProvider = true, renameProvider = { prepareProvider = true }, selectionRangeProvider = true, signatureHelpProvider = { triggerCharacters = { "(", "," } }, textDocumentSync = { change = 1, openClose = true, save = vim.empty_dict() }, typeDefinitionProvider = true, workspaceSymbolProvider = true}
[ INFO ] 2020-02-12T17:11:02Z+0900 ] /home/h-michael/.local/share/nvim/runtime/lua/vim/lsp.lua:504 ] "LSP[rust_analyzer]" "initialized" { resolved_capabilities = { code_action = true, document_formatting = true, document_highlight = true, document_range_formatting = false, document_symbol = true, find_references = true, goto_definition = true, hover = true, implementation = true, signature_help = true, signature_help_trigger_characters = { "(", "," }, text_document_did_change = 1, text_document_open_close = true, text_document_save = vim.empty_dict(), text_document_save_include_text = false, text_document_will_save = false, text_document_will_save_wait_until = false, workspace_symbol = true }}
[ DEBUG ] 2020-02-12T17:11:02Z+0900 ] .../h-michael/.local/share/nvim/runtime/lua/vim/lsp/rpc.lua:282 ] "rpc.notify" "textDocument/didOpen" { textDocument = { languageId = "rust", text = "//! The top-level documentation resides on the [project README](https://github.com/graphql-rust/graphql-client) at the moment.\n//!\n//! The main interface to this library is the custom derive that generates modules from a GraphQL query and schema. See the docs for the [`GraphQLQuery`] trait for a full example.\n\n#![deny(missing_docs)]\n#![deny(rust_2018_idioms)]\n#![deny(warnings)]\n\n#[allow(unused_imports)]\n#[macro_use]\nextern crate graphql_query_derive;\n\n#[doc(hidden)]\npub use graphql_query_derive::*;\n\nuse serde::*;\n\n#[cfg(feature = \"web\")]\npub mod web;\n\nuse std::collections::HashMap;\nuse std::fmt::{self, Display};\n\ndoc_comment::doctest!(\"../../README.md\");\n\n/// A convenience trait that can be used to build a GraphQL request body.\n///\n/// This will be implemented for you by codegen in the normal case. It is implemented on the struct you place the derive on.\n///\n/// Example:\n///\n/// ```\n/// use graphql_client::*;\n/// use serde_json::json;\n///\n/// #[derive(GraphQLQuery)]\n/// #[graphql(\n/// query_path = \"../graphql_client_codegen/src/tests/star_wars_query.graphql\",\n/// schema_path = \"../graphql_client_codegen/src/tests/star_wars_schema.graphql\"\n/// )]\n/// struct StarWarsQuery;\n///\n/// fn main() -> Result<(), failure::Error> {\n/// use graphql_client::GraphQLQuery;\n///\n/// let variables = star_wars_query::Variables {\n/// episode_for_hero: star_wars_query::Episode::NEWHOPE,\n/// };\n///\n/// let expected_body = json!({\n/// \"operationName\": star_wars_query::OPERATION_NAME,\n/// \"query\": star_wars_query::QUERY,\n/// \"variables\": {\n/// \"episodeForHero\": \"NEWHOPE\"\n/// },\n/// });\n///\n/// let actual_body = serde_json::to_value(\n/// StarWarsQuery::build_query(variables)\n/// )?;\n///\n/// assert_eq!(actual_body, expected_body);\n///\n/// Ok(())\n/// }\n/// ```\npub trait GraphQLQuery {\n /// The shape of the variables expected by the query. This should be a generated struct most of the time.\n type Variables: serde::Serialize;\n /// The top-level shape of the response data (the `data` field in the GraphQL response). In practice this should be generated, since it is hard to write by hand without error.\n type ResponseData: for<'de> serde::Deserialize<'de>;\n\n /// Produce a GraphQL query struct that can be JSON serialized and sent to a GraphQL API.\n fn build_query(variables: Self::Variables) -> QueryBody<Self::Variables>;\n}\n\n/// The form in which queries are sent over HTTP in most implementations. This will be built using the [`GraphQLQuery`] trait normally.\n#[derive(Debug, Serialize, Deserialize)]\npub struct QueryBody<Variables> {\n /// The values for the variables. They must match those declared in the queries. This should be the `Variables` struct from the generated module corresponding to the query.\n pub variables: Variables,\n /// The GraphQL query, as a string.\n pub query: &'static str,\n /// The GraphQL operation name, as a string.\n #[serde(rename = \"operationName\")]\n pub operation_name: &'static str,\n}\n\n/// Represents a location inside a query string. Used in errors. See [`Error`].\n#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq)]\npub struct Location {\n /// The line number in the query string where the error originated (starting from 1).\n pub line: i32,\n /// The column number in the query string where the error originated (starting from 1).\n pub column: i32,\n}\n\n/// Part of a path in a query. It can be an object key or an array index. See [`Error`].\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]\n#[serde(untagged)]\npub enum PathFragment {\n /// A key inside an object\n Key(String),\n /// An index inside an array\n Index(i32),\n}\n\nimpl Display for PathFragment {\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n match *self {\n PathFragment::Key(ref key) => write!(f, \"{}\", key),\n PathFragment::Index(ref idx) => write!(f, \"{}\", idx),\n }\n }\n}\n\n/// An element in the top-level `errors` array of a response body.\n///\n/// This tries to be as close to the spec as possible.\n///\n/// [Spec](https://github.com/facebook/graphql/blob/master/spec/Section%207%20--%20Response.md)\n///\n///\n/// ```\n/// # use serde_json::json;\n/// # use serde::Deserialize;\n/// # use graphql_client::GraphQLQuery;\n/// #\n/// # #[derive(Debug, Deserialize, PartialEq)]\n/// # struct ResponseData {\n/// # something: i32\n/// # }\n/// #\n/// # fn main() -> Result<(), failure::Error> {\n/// use graphql_client::*;\n///\n/// let body: Response<ResponseData> = serde_json::from_value(json!({\n/// \"data\": null,\n/// \"errors\": [\n/// {\n/// \"message\": \"The server crashed. Sorry.\",\n/// \"locations\": [{ \"line\": 1, \"column\": 1 }]\n/// },\n/// {\n/// \"message\": \"Seismic activity detected\",\n/// \"path\": [\"underground\", 20]\n/// },\n/// ],\n/// }))?;\n///\n/// let expected: Response<ResponseData> = Response {\n/// data: None,\n/// errors: Some(vec![\n/// Error {\n/// message: \"The server crashed. Sorry.\".to_owned(),\n/// locations: Some(vec![\n/// Location {\n/// line: 1,\n/// column: 1,\n/// }\n/// ]),\n/// path: None,\n/// extensions: None,\n/// },\n/// Error {\n/// message: \"Seismic activity detected\".to_owned(),\n/// locations: None,\n/// path: Some(vec![\n/// PathFragment::Key(\"underground\".into()),\n/// PathFragment::Index(20),\n/// ]),\n/// extensions: None,\n/// },\n/// ]),\n/// };\n///\n/// assert_eq!(body, expected);\n///\n/// # Ok(())\n/// # }\n/// ```\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]\npub struct Error {\n /// The human-readable error message. This is the only required field.\n pub message: String,\n /// Which locations in the query the error applies to.\n pub locations: Option<Vec<Location>>,\n /// Which path in the query the error applies to, e.g. `[\"users\", 0, \"email\"]`.\n pub path: Option<Vec<PathFragment>>,\n /// Additional errors. Their exact format is defined by the server.\n pub extensions: Option<HashMap<String, serde_json::Value>>,\n}\n\nimpl Display for Error {\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n // Use `/` as a separator like JSON Pointer.\n let path = self\n .path\n .as_ref()\n .map(|fragments| {\n fragments\n .iter()\n .fold(String::new(), |mut acc, item| {\n acc.push_str(&format!(\"{}/\", item));\n acc\n })\n .trim_end_matches('/')\n .to_string()\n })\n .unwrap_or_else(|| \"<query>\".to_string());\n\n // Get the location of the error. We'll use just the first location for this.\n let loc = self\n .locations\n .as_ref()\n .and_then(|locations| locations.iter().next())\n .cloned()\n .unwrap_or_else(Location::default);\n\n write!(f, \"{}:{}:{}: {}\", path, loc.line, loc.column, self.message)\n }\n}\n\n/// The generic shape taken by the responses of GraphQL APIs.\n///\n/// This will generally be used with the `ResponseData` struct from a derived module.\n///\n/// [Spec](https://github.com/facebook/graphql/blob/master/spec/Section%207%20--%20Response.md)\n///\n/// ```\n/// # use serde_json::json;\n/// # use serde::Deserialize;\n/// # use graphql_client::GraphQLQuery;\n/// #\n/// # #[derive(Debug, Deserialize, PartialEq)]\n/// # struct User {\n/// # id: i32,\n/// # }\n/// #\n/// # #[derive(Debug, Deserialize, PartialEq)]\n/// # struct Dog {\n/// # name: String\n/// # }\n/// #\n/// # #[derive(Debug, Deserialize, PartialEq)]\n/// # struct ResponseData {\n/// # users: Vec<User>,\n/// # dogs: Vec<Dog>,\n/// # }\n/// #\n/// # fn main() -> Result<(), failure::Error> {\n/// use graphql_client::Response;\n///\n/// let body: Response<ResponseData> = serde_json::from_value(json!({\n/// \"data\": {\n/// \"users\": [{\"id\": 13}],\n/// \"dogs\": [{\"name\": \"Strelka\"}],\n/// },\n/// \"errors\": [],\n/// }))?;\n///\n/// let expected: Response<ResponseData> = Response {\n/// data: Some(ResponseData {\n/// users: vec![User { id: 13 }],\n/// dogs: vec![Dog { name: \"Strelka\".to_owned() }],\n/// }),\n/// errors: Some(vec![]),\n/// };\n///\n/// assert_eq!(body, expected);\n///\n/// # Ok(())\n/// # }\n/// ```\n#[derive(Debug, Serialize, Deserialize, PartialEq)]\npub struct Response<Data> {\n /// The absent, partial or complete response data.\n pub data: Option<Data>,\n /// The top-level errors returned by the server.\n pub errors: Option<Vec<Error>>,\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use serde_json::json;\n\n #[test]\n fn graphql_error_works_with_just_message() {\n let err = json!({\n \"message\": \"I accidentally your whole query\"\n });\n\n let deserialized_error: Error = serde_json::from_value(err).unwrap();\n\n assert_eq!(\n deserialized_error,\n Error {\n message: \"I accidentally your whole query\".to_string(),\n locations: None,\n path: None,\n extensions: None,\n }\n )\n }\n\n #[test]\n fn full_graphql_error_deserialization() {\n let err = json!({\n \"message\": \"I accidentally your whole query\",\n \"locations\": [{ \"line\": 3, \"column\": 13}, {\"line\": 56, \"column\": 1}],\n \"path\": [\"home\", \"alone\", 3, \"rating\"]\n });\n\n let deserialized_error: Error = serde_json::from_value(err).unwrap();\n\n assert_eq!(\n deserialized_error,\n Error {\n message: \"I accidentally your whole query\".to_string(),\n locations: Some(vec![\n Location {\n line: 3,\n column: 13,\n },\n Location {\n line: 56,\n column: 1,\n },\n ]),\n path: Some(vec![\n PathFragment::Key(\"home\".to_owned()),\n PathFragment::Key(\"alone\".to_owned()),\n PathFragment::Index(3),\n PathFragment::Key(\"rating\".to_owned()),\n ]),\n extensions: None,\n }\n )\n }\n\n #[test]\n fn full_graphql_error_with_extensions_deserialization() {\n let err = json!({\n \"message\": \"I accidentally your whole query\",\n \"locations\": [{ \"line\": 3, \"column\": 13}, {\"line\": 56, \"column\": 1}],\n \"path\": [\"home\", \"alone\", 3, \"rating\"],\n \"extensions\": {\n \"code\": \"CAN_NOT_FETCH_BY_ID\",\n \"timestamp\": \"Fri Feb 9 14:33:09 UTC 2018\"\n }\n });\n\n let deserialized_error: Error = serde_json::from_value(err).unwrap();\n\n let mut expected_extensions = HashMap::new();\n expected_extensions.insert(\"code\".to_owned(), json!(\"CAN_NOT_FETCH_BY_ID\"));\n expected_extensions.insert(\"timestamp\".to_owned(), json!(\"Fri Feb 9 14:33:09 UTC 2018\"));\n let expected_extensions = Some(expected_extensions);\n\n assert_eq!(\n deserialized_error,\n Error {\n message: \"I accidentally your whole query\".to_string(),\n locations: Some(vec![\n Location {\n line: 3,\n column: 13,\n },\n Location {\n line: 56,\n column: 1,\n },\n ]),\n path: Some(vec![\n PathFragment::Key(\"home\".to_owned()),\n PathFragment::Key(\"alone\".to_owned()),\n PathFragment::Index(3),\n PathFragment::Key(\"rating\".to_owned()),\n ]),\n extensions: expected_extensions,\n }\n )\n }\n}\n", uri = "file:///home/h-michael/go/src/github.com/h-michael/graphql-client/graphql_client/src/lib.rs", version = 0 }}
[ DEBUG ] 2020-02-12T17:11:02Z+0900 ] .../h-michael/.local/share/nvim/runtime/lua/vim/lsp/rpc.lua:271 ] "rpc.send.payload" { jsonrpc = "2.0", method = "textDocument/didOpen", params = { textDocument = { languageId = "rust", text = "//! The top-level documentation resides on the [project README](https://github.com/graphql-rust/graphql-client) at the moment.\n//!\n//! The main interface to this library is the custom derive that generates modules from a GraphQL query and schema. See the docs for the [`GraphQLQuery`] trait for a full example.\n\n#![deny(missing_docs)]\n#![deny(rust_2018_idioms)]\n#![deny(warnings)]\n\n#[allow(unused_imports)]\n#[macro_use]\nextern crate graphql_query_derive;\n\n#[doc(hidden)]\npub use graphql_query_derive::*;\n\nuse serde::*;\n\n#[cfg(feature = \"web\")]\npub mod web;\n\nuse std::collections::HashMap;\nuse std::fmt::{self, Display};\n\ndoc_comment::doctest!(\"../../README.md\");\n\n/// A convenience trait that can be used to build a GraphQL request body.\n///\n/// This will be implemented for you by codegen in the normal case. It is implemented on the struct you place the derive on.\n///\n/// Example:\n///\n/// ```\n/// use graphql_client::*;\n/// use serde_json::json;\n///\n/// #[derive(GraphQLQuery)]\n/// #[graphql(\n/// query_path = \"../graphql_client_codegen/src/tests/star_wars_query.graphql\",\n/// schema_path = \"../graphql_client_codegen/src/tests/star_wars_schema.graphql\"\n/// )]\n/// struct StarWarsQuery;\n///\n/// fn main() -> Result<(), failure::Error> {\n/// use graphql_client::GraphQLQuery;\n///\n/// let variables = star_wars_query::Variables {\n/// episode_for_hero: star_wars_query::Episode::NEWHOPE,\n/// };\n///\n/// let expected_body = json!({\n/// \"operationName\": star_wars_query::OPERATION_NAME,\n/// \"query\": star_wars_query::QUERY,\n/// \"variables\": {\n/// \"episodeForHero\": \"NEWHOPE\"\n/// },\n/// });\n///\n/// let actual_body = serde_json::to_value(\n/// StarWarsQuery::build_query(variables)\n/// )?;\n///\n/// assert_eq!(actual_body, expected_body);\n///\n/// Ok(())\n/// }\n/// ```\npub trait GraphQLQuery {\n /// The shape of the variables expected by the query. This should be a generated struct most of the time.\n type Variables: serde::Serialize;\n /// The top-level shape of the response data (the `data` field in the GraphQL response). In practice this should be generated, since it is hard to write by hand without error.\n type ResponseData: for<'de> serde::Deserialize<'de>;\n\n /// Produce a GraphQL query struct that can be JSON serialized and sent to a GraphQL API.\n fn build_query(variables: Self::Variables) -> QueryBody<Self::Variables>;\n}\n\n/// The form in which queries are sent over HTTP in most implementations. This will be built using the [`GraphQLQuery`] trait normally.\n#[derive(Debug, Serialize, Deserialize)]\npub struct QueryBody<Variables> {\n /// The values for the variables. They must match those declared in the queries. This should be the `Variables` struct from the generated module corresponding to the query.\n pub variables: Variables,\n /// The GraphQL query, as a string.\n pub query: &'static str,\n /// The GraphQL operation name, as a string.\n #[serde(rename = \"operationName\")]\n pub operation_name: &'static str,\n}\n\n/// Represents a location inside a query string. Used in errors. See [`Error`].\n#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq)]\npub struct Location {\n /// The line number in the query string where the error originated (starting from 1).\n pub line: i32,\n /// The column number in the query string where the error originated (starting from 1).\n pub column: i32,\n}\n\n/// Part of a path in a query. It can be an object key or an array index. See [`Error`].\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]\n#[serde(untagged)]\npub enum PathFragment {\n /// A key inside an object\n Key(String),\n /// An index inside an array\n Index(i32),\n}\n\nimpl Display for PathFragment {\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n match *self {\n PathFragment::Key(ref key) => write!(f, \"{}\", key),\n PathFragment::Index(ref idx) => write!(f, \"{}\", idx),\n }\n }\n}\n\n/// An element in the top-level `errors` array of a response body.\n///\n/// This tries to be as close to the spec as possible.\n///\n/// [Spec](https://github.com/facebook/graphql/blob/master/spec/Section%207%20--%20Response.md)\n///\n///\n/// ```\n/// # use serde_json::json;\n/// # use serde::Deserialize;\n/// # use graphql_client::GraphQLQuery;\n/// #\n/// # #[derive(Debug, Deserialize, PartialEq)]\n/// # struct ResponseData {\n/// # something: i32\n/// # }\n/// #\n/// # fn main() -> Result<(), failure::Error> {\n/// use graphql_client::*;\n///\n/// let body: Response<ResponseData> = serde_json::from_value(json!({\n/// \"data\": null,\n/// \"errors\": [\n/// {\n/// \"message\": \"The server crashed. Sorry.\",\n/// \"locations\": [{ \"line\": 1, \"column\": 1 }]\n/// },\n/// {\n/// \"message\": \"Seismic activity detected\",\n/// \"path\": [\"underground\", 20]\n/// },\n/// ],\n/// }))?;\n///\n/// let expected: Response<ResponseData> = Response {\n/// data: None,\n/// errors: Some(vec![\n/// Error {\n/// message: \"The server crashed. Sorry.\".to_owned(),\n/// locations: Some(vec![\n/// Location {\n/// line: 1,\n/// column: 1,\n/// }\n/// ]),\n/// path: None,\n/// extensions: None,\n/// },\n/// Error {\n/// message: \"Seismic activity detected\".to_owned(),\n/// locations: None,\n/// path: Some(vec![\n/// PathFragment::Key(\"underground\".into()),\n/// PathFragment::Index(20),\n/// ]),\n/// extensions: None,\n/// },\n/// ]),\n/// };\n///\n/// assert_eq!(body, expected);\n///\n/// # Ok(())\n/// # }\n/// ```\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]\npub struct Error {\n /// The human-readable error message. This is the only required field.\n pub message: String,\n /// Which locations in the query the error applies to.\n pub locations: Option<Vec<Location>>,\n /// Which path in the query the error applies to, e.g. `[\"users\", 0, \"email\"]`.\n pub path: Option<Vec<PathFragment>>,\n /// Additional errors. Their exact format is defined by the server.\n pub extensions: Option<HashMap<String, serde_json::Value>>,\n}\n\nimpl Display for Error {\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n // Use `/` as a separator like JSON Pointer.\n let path = self\n .path\n .as_ref()\n .map(|fragments| {\n fragments\n .iter()\n .fold(String::new(), |mut acc, item| {\n acc.push_str(&format!(\"{}/\", item));\n acc\n })\n .trim_end_matches('/')\n .to_string()\n })\n .unwrap_or_else(|| \"<query>\".to_string());\n\n // Get the location of the error. We'll use just the first location for this.\n let loc = self\n .locations\n .as_ref()\n .and_then(|locations| locations.iter().next())\n .cloned()\n .unwrap_or_else(Location::default);\n\n write!(f, \"{}:{}:{}: {}\", path, loc.line, loc.column, self.message)\n }\n}\n\n/// The generic shape taken by the responses of GraphQL APIs.\n///\n/// This will generally be used with the `ResponseData` struct from a derived module.\n///\n/// [Spec](https://github.com/facebook/graphql/blob/master/spec/Section%207%20--%20Response.md)\n///\n/// ```\n/// # use serde_json::json;\n/// # use serde::Deserialize;\n/// # use graphql_client::GraphQLQuery;\n/// #\n/// # #[derive(Debug, Deserialize, PartialEq)]\n/// # struct User {\n/// # id: i32,\n/// # }\n/// #\n/// # #[derive(Debug, Deserialize, PartialEq)]\n/// # struct Dog {\n/// # name: String\n/// # }\n/// #\n/// # #[derive(Debug, Deserialize, PartialEq)]\n/// # struct ResponseData {\n/// # users: Vec<User>,\n/// # dogs: Vec<Dog>,\n/// # }\n/// #\n/// # fn main() -> Result<(), failure::Error> {\n/// use graphql_client::Response;\n///\n/// let body: Response<ResponseData> = serde_json::from_value(json!({\n/// \"data\": {\n/// \"users\": [{\"id\": 13}],\n/// \"dogs\": [{\"name\": \"Strelka\"}],\n/// },\n/// \"errors\": [],\n/// }))?;\n///\n/// let expected: Response<ResponseData> = Response {\n/// data: Some(ResponseData {\n/// users: vec![User { id: 13 }],\n/// dogs: vec![Dog { name: \"Strelka\".to_owned() }],\n/// }),\n/// errors: Some(vec![]),\n/// };\n///\n/// assert_eq!(body, expected);\n///\n/// # Ok(())\n/// # }\n/// ```\n#[derive(Debug, Serialize, Deserialize, PartialEq)]\npub struct Response<Data> {\n /// The absent, partial or complete response data.\n pub data: Option<Data>,\n /// The top-level errors returned by the server.\n pub errors: Option<Vec<Error>>,\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use serde_json::json;\n\n #[test]\n fn graphql_error_works_with_just_message() {\n let err = json!({\n \"message\": \"I accidentally your whole query\"\n });\n\n let deserialized_error: Error = serde_json::from_value(err).unwrap();\n\n assert_eq!(\n deserialized_error,\n Error {\n message: \"I accidentally your whole query\".to_string(),\n locations: None,\n path: None,\n extensions: None,\n }\n )\n }\n\n #[test]\n fn full_graphql_error_deserialization() {\n let err = json!({\n \"message\": \"I accidentally your whole query\",\n \"locations\": [{ \"line\": 3, \"column\": 13}, {\"line\": 56, \"column\": 1}],\n \"path\": [\"home\", \"alone\", 3, \"rating\"]\n });\n\n let deserialized_error: Error = serde_json::from_value(err).unwrap();\n\n assert_eq!(\n deserialized_error,\n Error {\n message: \"I accidentally your whole query\".to_string(),\n locations: Some(vec![\n Location {\n line: 3,\n column: 13,\n },\n Location {\n line: 56,\n column: 1,\n },\n ]),\n path: Some(vec![\n PathFragment::Key(\"home\".to_owned()),\n PathFragment::Key(\"alone\".to_owned()),\n PathFragment::Index(3),\n PathFragment::Key(\"rating\".to_owned()),\n ]),\n extensions: None,\n }\n )\n }\n\n #[test]\n fn full_graphql_error_with_extensions_deserialization() {\n let err = json!({\n \"message\": \"I accidentally your whole query\",\n \"locations\": [{ \"line\": 3, \"column\": 13}, {\"line\": 56, \"column\": 1}],\n \"path\": [\"home\", \"alone\", 3, \"rating\"],\n \"extensions\": {\n \"code\": \"CAN_NOT_FETCH_BY_ID\",\n \"timestamp\": \"Fri Feb 9 14:33:09 UTC 2018\"\n }\n });\n\n let deserialized_error: Error = serde_json::from_value(err).unwrap();\n\n let mut expected_extensions = HashMap::new();\n expected_extensions.insert(\"code\".to_owned(), json!(\"CAN_NOT_FETCH_BY_ID\"));\n expected_extensions.insert(\"timestamp\".to_owned(), json!(\"Fri Feb 9 14:33:09 UTC 2018\"));\n let expected_extensions = Some(expected_extensions);\n\n assert_eq!(\n deserialized_error,\n Error {\n message: \"I accidentally your whole query\".to_string(),\n locations: Some(vec![\n Location {\n line: 3,\n column: 13,\n },\n Location {\n line: 56,\n column: 1,\n },\n ]),\n path: Some(vec![\n PathFragment::Key(\"home\".to_owned()),\n PathFragment::Key(\"alone\".to_owned()),\n PathFragment::Index(3),\n PathFragment::Key(\"rating\".to_owned()),\n ]),\n extensions: expected_extensions,\n }\n )\n }\n}\n", uri = "file:///home/h-michael/go/src/github.com/h-michael/graphql-client/graphql_client/src/lib.rs", version = 0 } }}
[ ERROR ] 2020-02-12T17:11:02Z+0900 ] .../h-michael/.local/share/nvim/runtime/lua/vim/lsp/rpc.lua:321 ] "rpc" "ra_lsp_server" "stderr" "[ERROR ra_lsp_server] Failed to deserialize config: invalid length 0, expected struct ServerConfig with 12 elements; []\n"
[ DEBUG ] 2020-02-12T17:11:02Z+0900 ] .../h-michael/.local/share/nvim/runtime/lua/vim/lsp/rpc.lua:351 ] "decoded" { jsonrpc = "2.0", method = "window/showMessage", params = { message = "Failed to deserialize config: invalid length 0, expected struct ServerConfig with 12 elements; []", type = 1 }}
[ DEBUG ] 2020-02-12T17:11:02Z+0900 ] /home/h-michael/.local/share/nvim/runtime/lua/vim/lsp.lua:371 ] "notification" "window/showMessage" { message = "Failed to deserialize config: invalid length 0, expected struct ServerConfig with 12 elements; []", type = 1}
[ DEBUG ] 2020-02-12T17:11:02Z+0900 ] ...hael/.local/share/nvim/runtime/lua/vim/lsp/callbacks.lua:246 ] "default_callback" "window/showMessage" { client_id = 1, params = { message = "Failed to deserialize config: invalid length 0, expected struct ServerConfig with 12 elements; []", type = 1 }}
[ DEBUG ] 2020-02-12T17:11:06Z+0900 ] .../h-michael/.local/share/nvim/runtime/lua/vim/lsp/rpc.lua:351 ] "decoded" { jsonrpc = "2.0", method = "window/showMessage", params = { message = "workspace loaded, 281 rust packages", type = 3 }}
[ DEBUG ] 2020-02-12T17:11:06Z+0900 ] /home/h-michael/.local/share/nvim/runtime/lua/vim/lsp.lua:371 ] "notification" "window/showMessage" { message = "workspace loaded, 281 rust packages", type = 3}
[ DEBUG ] 2020-02-12T17:11:06Z+0900 ] ...hael/.local/share/nvim/runtime/lua/vim/lsp/callbacks.lua:246 ] "default_callback" "window/showMessage" { client_id = 1, params = { message = "workspace loaded, 281 rust packages", type = 3 }}
[ DEBUG ] 2020-02-12T17:11:06Z+0900 ] .../h-michael/.local/share/nvim/runtime/lua/vim/lsp/rpc.lua:351 ] "decoded" { jsonrpc = "2.0", method = "$/progress", params = { token = "rustAnalyzer/cargoWatcher", value = { cancellable = false, kind = "begin", title = "Running 'cargo check'" } }}
[ DEBUG ] 2020-02-12T17:11:06Z+0900 ] /home/h-michael/.local/share/nvim/runtime/lua/vim/lsp.lua:371 ] "notification" "$/progress" { token = "rustAnalyzer/cargoWatcher", value = { cancellable = false, kind = "begin", title = "Running 'cargo check'" }}
[ DEBUG ] 2020-02-12T17:11:06Z+0900 ] .../h-michael/.local/share/nvim/runtime/lua/vim/lsp/rpc.lua:351 ] "decoded" { jsonrpc = "2.0", method = "$/progress", params = { token = "rustAnalyzer/cargoWatcher", value = { kind = "end" } }}
[ DEBUG ] 2020-02-12T17:11:06Z+0900 ] /home/h-michael/.local/share/nvim/runtime/lua/vim/lsp.lua:371 ] "notification" "$/progress" { token = "rustAnalyzer/cargoWatcher", value = { kind = "end" }}
[ INFO ] 2020-02-12T17:12:35Z+0900 ] /home/h-michael/.local/share/nvim/runtime/lua/vim/lsp.lua:800 ] "exit_handler" {}
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment